mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-07-01 18:52:20 +08:00
fix: require resolve (#4356)
* fix: require resolve * fix: error * fix: skip realpath * fix: fs.realpath
This commit is contained in:
parent
cb8aa0d931
commit
6e3595c0be
@ -27,7 +27,11 @@ export default (app: Application) => {
|
|||||||
const upgrading = await fsExists(file);
|
const upgrading = await fsExists(file);
|
||||||
if (upgrading) {
|
if (upgrading) {
|
||||||
await app.upgrade();
|
await app.upgrade();
|
||||||
await fs.promises.rm(file);
|
try {
|
||||||
|
await fs.promises.rm(file);
|
||||||
|
} catch (error) {
|
||||||
|
// skip
|
||||||
|
}
|
||||||
} else if (options.quickstart) {
|
} else if (options.quickstart) {
|
||||||
if (await app.isInstalled()) {
|
if (await app.isInstalled()) {
|
||||||
await app.upgrade();
|
await app.upgrade();
|
||||||
|
@ -16,7 +16,7 @@ import fg from 'fast-glob';
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import net from 'net';
|
import net from 'net';
|
||||||
import { basename, dirname, join, resolve, sep } from 'path';
|
import { basename, join, resolve, sep } from 'path';
|
||||||
import Application from '../application';
|
import Application from '../application';
|
||||||
import { createAppProxy, tsxRerunning } from '../helper';
|
import { createAppProxy, tsxRerunning } from '../helper';
|
||||||
import { Plugin } from '../plugin';
|
import { Plugin } from '../plugin';
|
||||||
@ -29,6 +29,7 @@ import {
|
|||||||
copyTempPackageToStorageAndLinkToNodeModules,
|
copyTempPackageToStorageAndLinkToNodeModules,
|
||||||
downloadAndUnzipToTempDir,
|
downloadAndUnzipToTempDir,
|
||||||
getNpmInfo,
|
getNpmInfo,
|
||||||
|
getPluginBasePath,
|
||||||
getPluginInfoByNpm,
|
getPluginInfoByNpm,
|
||||||
removeTmpDir,
|
removeTmpDir,
|
||||||
updatePluginByCompressedFileUrl,
|
updatePluginByCompressedFileUrl,
|
||||||
@ -395,7 +396,7 @@ export class PluginManager {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
async loadCommands() {
|
async loadCommands() {
|
||||||
this.app.log.debug('load commands');
|
this.app.log.info('load commands');
|
||||||
const items = await this.repository.find({
|
const items = await this.repository.find({
|
||||||
filter: {
|
filter: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@ -404,21 +405,16 @@ export class PluginManager {
|
|||||||
const packageNames: string[] = items.map((item) => item.packageName);
|
const packageNames: string[] = items.map((item) => item.packageName);
|
||||||
const source = [];
|
const source = [];
|
||||||
for (const packageName of packageNames) {
|
for (const packageName of packageNames) {
|
||||||
const file = require.resolve(packageName);
|
const dirname = await getPluginBasePath(packageName);
|
||||||
const sourceDir = basename(dirname(file)) === 'src' ? 'src' : 'dist';
|
const directory = join(dirname, 'server/commands/*.' + (basename(dirname) === 'src' ? 'ts' : 'js'));
|
||||||
const directory = join(
|
|
||||||
packageName,
|
|
||||||
sourceDir,
|
|
||||||
'server/commands/*.' + (basename(dirname(file)) === 'src' ? 'ts' : 'js'),
|
|
||||||
);
|
|
||||||
source.push(directory.replaceAll(sep, '/'));
|
source.push(directory.replaceAll(sep, '/'));
|
||||||
}
|
}
|
||||||
for (const plugin of this.options.plugins || []) {
|
for (const plugin of this.options.plugins || []) {
|
||||||
if (typeof plugin === 'string') {
|
if (typeof plugin === 'string') {
|
||||||
const packageName = await PluginManager.getPackageName(plugin);
|
const { packageName } = await PluginManager.parseName(plugin);
|
||||||
const file = require.resolve(packageName);
|
const dirname = await getPluginBasePath(packageName);
|
||||||
const sourceDir = basename(dirname(file)) === 'src' ? 'src' : 'lib';
|
const directory = join(dirname, 'server/commands/*.' + (basename(dirname) === 'src' ? 'ts' : 'js'));
|
||||||
const directory = join(packageName, sourceDir, 'server/commands/*.' + (sourceDir === 'src' ? 'ts' : 'js'));
|
|
||||||
source.push(directory.replaceAll(sep, '/'));
|
source.push(directory.replaceAll(sep, '/'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
/* istanbul ignore next -- @preserve */
|
/* istanbul ignore next -- @preserve */
|
||||||
|
|
||||||
import { importModule, isURL } from '@nocobase/utils';
|
import { importModule, isURL, requireResolve } from '@nocobase/utils';
|
||||||
import { createStoragePluginSymLink } from '@nocobase/utils/plugin-symlink';
|
import { createStoragePluginSymLink } from '@nocobase/utils/plugin-symlink';
|
||||||
import axios, { AxiosRequestConfig } from 'axios';
|
import axios, { AxiosRequestConfig } from 'axios';
|
||||||
import decompress from 'decompress';
|
import decompress from 'decompress';
|
||||||
@ -585,3 +585,19 @@ export async function checkAndGetCompatible(packageName: string) {
|
|||||||
depsCompatible: compatible,
|
depsCompatible: compatible,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getPluginBasePath(packageName: string) {
|
||||||
|
if (!packageName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const file = await fs.realpath(await requireResolve(packageName));
|
||||||
|
try {
|
||||||
|
const basePath = await fs.realpath(path.resolve(process.env.NODE_MODULES_PATH, packageName, 'src'));
|
||||||
|
if (file.startsWith(basePath)) {
|
||||||
|
return basePath;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// skip
|
||||||
|
}
|
||||||
|
return path.dirname(path.dirname(file));
|
||||||
|
}
|
||||||
|
@ -11,14 +11,13 @@
|
|||||||
|
|
||||||
import { Model } from '@nocobase/database';
|
import { Model } from '@nocobase/database';
|
||||||
import { LoggerOptions } from '@nocobase/logger';
|
import { LoggerOptions } from '@nocobase/logger';
|
||||||
import { fsExists, importModule } from '@nocobase/utils';
|
import { fsExists } from '@nocobase/utils';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import glob from 'glob';
|
|
||||||
import type { TFuncKey, TOptions } from 'i18next';
|
import type { TFuncKey, TOptions } from 'i18next';
|
||||||
import { basename, resolve } from 'path';
|
import { resolve } from 'path';
|
||||||
import { Application } from './application';
|
import { Application } from './application';
|
||||||
import { getExposeChangelogUrl, getExposeReadmeUrl, InstallOptions } from './plugin-manager';
|
import { InstallOptions, getExposeChangelogUrl, getExposeReadmeUrl } from './plugin-manager';
|
||||||
import { checkAndGetCompatible } from './plugin-manager/utils';
|
import { checkAndGetCompatible, getPluginBasePath } from './plugin-manager/utils';
|
||||||
|
|
||||||
export interface PluginInterface {
|
export interface PluginInterface {
|
||||||
beforeLoad?: () => void;
|
beforeLoad?: () => void;
|
||||||
@ -146,46 +145,16 @@ export abstract class Plugin<O = any> implements PluginInterface {
|
|||||||
this.options = options || {};
|
this.options = options || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
async loadCommands() {
|
|
||||||
const extensions = ['js', 'ts'];
|
|
||||||
const directory = resolve(
|
|
||||||
process.env.NODE_MODULES_PATH,
|
|
||||||
this.options.packageName,
|
|
||||||
await this.getSourceDir(),
|
|
||||||
'server/commands',
|
|
||||||
);
|
|
||||||
const patten = `${directory}/*.{${extensions.join(',')}}`;
|
|
||||||
const files = glob.sync(patten, {
|
|
||||||
ignore: ['**/*.d.ts'],
|
|
||||||
});
|
|
||||||
for (const file of files) {
|
|
||||||
let filename = basename(file);
|
|
||||||
filename = filename.substring(0, filename.lastIndexOf('.')) || filename;
|
|
||||||
const callback = await importModule(file);
|
|
||||||
callback(this.app);
|
|
||||||
}
|
|
||||||
if (files.length) {
|
|
||||||
this.app.log.debug(`load commands [${this.name}]`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
async loadMigrations() {
|
async loadMigrations() {
|
||||||
this.app.log.debug(`load plugin migrations [${this.name}]`);
|
this.app.log.debug(`load plugin migrations [${this.name}]`);
|
||||||
if (!this.options.packageName) {
|
const basePath = await this.getPluginBasePath();
|
||||||
|
if (!basePath) {
|
||||||
return { beforeLoad: [], afterSync: [], afterLoad: [] };
|
return { beforeLoad: [], afterSync: [], afterLoad: [] };
|
||||||
}
|
}
|
||||||
const directory = resolve(
|
const directory = resolve(basePath, 'server/migrations');
|
||||||
process.env.NODE_MODULES_PATH,
|
|
||||||
this.options.packageName,
|
|
||||||
await this.getSourceDir(),
|
|
||||||
'server/migrations',
|
|
||||||
);
|
|
||||||
return await this.app.loadMigrations({
|
return await this.app.loadMigrations({
|
||||||
directory,
|
directory,
|
||||||
namespace: this.options.packageName,
|
namespace: this.options.packageName,
|
||||||
@ -195,19 +164,22 @@ export abstract class Plugin<O = any> implements PluginInterface {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async getPluginBasePath() {
|
||||||
|
if (!this.options.packageName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return getPluginBasePath(this.options.packageName);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
async loadCollections() {
|
async loadCollections() {
|
||||||
if (!this.options.packageName) {
|
const basePath = await this.getPluginBasePath();
|
||||||
|
if (!basePath) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const directory = resolve(
|
const directory = resolve(basePath, 'server/collections');
|
||||||
process.env.NODE_MODULES_PATH,
|
|
||||||
this.options.packageName,
|
|
||||||
await this.getSourceDir(),
|
|
||||||
'server/collections',
|
|
||||||
);
|
|
||||||
if (await fsExists(directory)) {
|
if (await fsExists(directory)) {
|
||||||
await this.db.import({
|
await this.db.import({
|
||||||
directory,
|
directory,
|
||||||
@ -265,38 +237,6 @@ export abstract class Plugin<O = any> implements PluginInterface {
|
|||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
protected async getSourceDir() {
|
|
||||||
if (this._sourceDir) {
|
|
||||||
return this._sourceDir;
|
|
||||||
}
|
|
||||||
if (await this.isDev()) {
|
|
||||||
return (this._sourceDir = 'src');
|
|
||||||
}
|
|
||||||
if (basename(__dirname) === 'src') {
|
|
||||||
return (this._sourceDir = 'src');
|
|
||||||
}
|
|
||||||
return (this._sourceDir = this.isPreset ? 'lib' : 'dist');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
protected async isDev() {
|
|
||||||
if (!this.options.packageName) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const file = await fs.promises.realpath(
|
|
||||||
resolve(process.env.NODE_MODULES_PATH || resolve(process.cwd(), 'node_modules'), this.options.packageName),
|
|
||||||
);
|
|
||||||
if (file.startsWith(resolve(process.cwd(), 'packages'))) {
|
|
||||||
return !!process.env.IS_DEV_CMD;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Plugin;
|
export default Plugin;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
const { dirname, resolve } = require('path');
|
const { dirname, resolve } = require('path');
|
||||||
const { readFile, writeFile, readdir, symlink, unlink, mkdir, stat } = require('fs').promises;
|
const { realpath, readFile, writeFile, readdir, symlink, unlink, mkdir, stat } = require('fs').promises;
|
||||||
|
|
||||||
async function getStoragePluginNames(target) {
|
async function getStoragePluginNames(target) {
|
||||||
const plugins = [];
|
const plugins = [];
|
||||||
@ -76,6 +76,10 @@ async function createDevPluginSymLink(pluginName) {
|
|||||||
}
|
}
|
||||||
const link = resolve(nodeModulesPath, pluginName);
|
const link = resolve(nodeModulesPath, pluginName);
|
||||||
if (await fsExists(link)) {
|
if (await fsExists(link)) {
|
||||||
|
const real = await realpath(link);
|
||||||
|
if (real === resolve(packagePluginsPath, pluginName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
await unlink(link);
|
await unlink(link);
|
||||||
}
|
}
|
||||||
await symlink(resolve(packagePluginsPath, pluginName), link, 'dir');
|
await symlink(resolve(packagePluginsPath, pluginName), link, 'dir');
|
||||||
|
@ -7,9 +7,25 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { pathToFileURL } from 'url';
|
import { pathToFileURL } from 'url';
|
||||||
|
|
||||||
|
export async function requireResolve(m: any) {
|
||||||
|
if (!process.env.VITEST) {
|
||||||
|
return require.resolve(m);
|
||||||
|
}
|
||||||
|
// 以下逻辑仅用于测试环境,因为 vitest 不支持对 require 调用进行别名
|
||||||
|
const json = JSON.parse(
|
||||||
|
await fs.promises.readFile(path.resolve(process.cwd(), './tsconfig.paths.json'), { encoding: 'utf8' }),
|
||||||
|
);
|
||||||
|
const paths = json.compilerOptions.paths;
|
||||||
|
if (paths[m]) {
|
||||||
|
return require.resolve(path.resolve(process.cwd(), paths[m][0], 'index.ts'));
|
||||||
|
}
|
||||||
|
return require.resolve(m);
|
||||||
|
}
|
||||||
|
|
||||||
export function requireModule(m: any) {
|
export function requireModule(m: any) {
|
||||||
if (typeof m === 'string') {
|
if (typeof m === 'string') {
|
||||||
m = require(m);
|
m = require(m);
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the NocoBase (R) project.
|
||||||
|
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||||
|
* Authors: NocoBase Team.
|
||||||
|
*
|
||||||
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||||
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { MockServer, createMockServer } from '@nocobase/test';
|
||||||
|
|
||||||
|
describe('cli', () => {
|
||||||
|
let app: MockServer;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
app = await createMockServer({
|
||||||
|
plugins: ['nocobase'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await app.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('find', async () => {
|
||||||
|
try {
|
||||||
|
await app.runCommand('restore');
|
||||||
|
} catch (error) {
|
||||||
|
// skip
|
||||||
|
}
|
||||||
|
const command = app.findCommand('restore');
|
||||||
|
expect(command).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
@ -87,14 +87,9 @@ export class PresetNocoBase extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getPackageJson(name) {
|
async getPackageJson(name) {
|
||||||
let packageName = name;
|
const { packageName } = await PluginManager.parseName(name);
|
||||||
try {
|
|
||||||
packageName = await PluginManager.getPackageName(name);
|
|
||||||
} catch (error) {
|
|
||||||
packageName = name;
|
|
||||||
}
|
|
||||||
const packageJson = await PluginManager.getPackageJson(packageName);
|
const packageJson = await PluginManager.getPackageJson(packageName);
|
||||||
return packageJson;
|
return { ...packageJson, name: packageName };
|
||||||
}
|
}
|
||||||
|
|
||||||
async allPlugins() {
|
async allPlugins() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user