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);
|
||||
if (upgrading) {
|
||||
await app.upgrade();
|
||||
await fs.promises.rm(file);
|
||||
try {
|
||||
await fs.promises.rm(file);
|
||||
} catch (error) {
|
||||
// skip
|
||||
}
|
||||
} else if (options.quickstart) {
|
||||
if (await app.isInstalled()) {
|
||||
await app.upgrade();
|
||||
|
@ -16,7 +16,7 @@ import fg from 'fast-glob';
|
||||
import fs from 'fs';
|
||||
import _ from 'lodash';
|
||||
import net from 'net';
|
||||
import { basename, dirname, join, resolve, sep } from 'path';
|
||||
import { basename, join, resolve, sep } from 'path';
|
||||
import Application from '../application';
|
||||
import { createAppProxy, tsxRerunning } from '../helper';
|
||||
import { Plugin } from '../plugin';
|
||||
@ -29,6 +29,7 @@ import {
|
||||
copyTempPackageToStorageAndLinkToNodeModules,
|
||||
downloadAndUnzipToTempDir,
|
||||
getNpmInfo,
|
||||
getPluginBasePath,
|
||||
getPluginInfoByNpm,
|
||||
removeTmpDir,
|
||||
updatePluginByCompressedFileUrl,
|
||||
@ -395,7 +396,7 @@ export class PluginManager {
|
||||
* @internal
|
||||
*/
|
||||
async loadCommands() {
|
||||
this.app.log.debug('load commands');
|
||||
this.app.log.info('load commands');
|
||||
const items = await this.repository.find({
|
||||
filter: {
|
||||
enabled: true,
|
||||
@ -404,21 +405,16 @@ export class PluginManager {
|
||||
const packageNames: string[] = items.map((item) => item.packageName);
|
||||
const source = [];
|
||||
for (const packageName of packageNames) {
|
||||
const file = require.resolve(packageName);
|
||||
const sourceDir = basename(dirname(file)) === 'src' ? 'src' : 'dist';
|
||||
const directory = join(
|
||||
packageName,
|
||||
sourceDir,
|
||||
'server/commands/*.' + (basename(dirname(file)) === 'src' ? 'ts' : 'js'),
|
||||
);
|
||||
const dirname = await getPluginBasePath(packageName);
|
||||
const directory = join(dirname, 'server/commands/*.' + (basename(dirname) === 'src' ? 'ts' : 'js'));
|
||||
|
||||
source.push(directory.replaceAll(sep, '/'));
|
||||
}
|
||||
for (const plugin of this.options.plugins || []) {
|
||||
if (typeof plugin === 'string') {
|
||||
const packageName = await PluginManager.getPackageName(plugin);
|
||||
const file = require.resolve(packageName);
|
||||
const sourceDir = basename(dirname(file)) === 'src' ? 'src' : 'lib';
|
||||
const directory = join(packageName, sourceDir, 'server/commands/*.' + (sourceDir === 'src' ? 'ts' : 'js'));
|
||||
const { packageName } = await PluginManager.parseName(plugin);
|
||||
const dirname = await getPluginBasePath(packageName);
|
||||
const directory = join(dirname, 'server/commands/*.' + (basename(dirname) === 'src' ? 'ts' : 'js'));
|
||||
source.push(directory.replaceAll(sep, '/'));
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
/* 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 axios, { AxiosRequestConfig } from 'axios';
|
||||
import decompress from 'decompress';
|
||||
@ -585,3 +585,19 @@ export async function checkAndGetCompatible(packageName: string) {
|
||||
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 { LoggerOptions } from '@nocobase/logger';
|
||||
import { fsExists, importModule } from '@nocobase/utils';
|
||||
import { fsExists } from '@nocobase/utils';
|
||||
import fs from 'fs';
|
||||
import glob from 'glob';
|
||||
import type { TFuncKey, TOptions } from 'i18next';
|
||||
import { basename, resolve } from 'path';
|
||||
import { resolve } from 'path';
|
||||
import { Application } from './application';
|
||||
import { getExposeChangelogUrl, getExposeReadmeUrl, InstallOptions } from './plugin-manager';
|
||||
import { checkAndGetCompatible } from './plugin-manager/utils';
|
||||
import { InstallOptions, getExposeChangelogUrl, getExposeReadmeUrl } from './plugin-manager';
|
||||
import { checkAndGetCompatible, getPluginBasePath } from './plugin-manager/utils';
|
||||
|
||||
export interface PluginInterface {
|
||||
beforeLoad?: () => void;
|
||||
@ -146,46 +145,16 @@ export abstract class Plugin<O = any> implements PluginInterface {
|
||||
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
|
||||
*/
|
||||
async loadMigrations() {
|
||||
this.app.log.debug(`load plugin migrations [${this.name}]`);
|
||||
if (!this.options.packageName) {
|
||||
const basePath = await this.getPluginBasePath();
|
||||
if (!basePath) {
|
||||
return { beforeLoad: [], afterSync: [], afterLoad: [] };
|
||||
}
|
||||
const directory = resolve(
|
||||
process.env.NODE_MODULES_PATH,
|
||||
this.options.packageName,
|
||||
await this.getSourceDir(),
|
||||
'server/migrations',
|
||||
);
|
||||
const directory = resolve(basePath, 'server/migrations');
|
||||
return await this.app.loadMigrations({
|
||||
directory,
|
||||
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
|
||||
*/
|
||||
async loadCollections() {
|
||||
if (!this.options.packageName) {
|
||||
const basePath = await this.getPluginBasePath();
|
||||
if (!basePath) {
|
||||
return;
|
||||
}
|
||||
const directory = resolve(
|
||||
process.env.NODE_MODULES_PATH,
|
||||
this.options.packageName,
|
||||
await this.getSourceDir(),
|
||||
'server/collections',
|
||||
);
|
||||
const directory = resolve(basePath, 'server/collections');
|
||||
if (await fsExists(directory)) {
|
||||
await this.db.import({
|
||||
directory,
|
||||
@ -265,38 +237,6 @@ export abstract class Plugin<O = any> implements PluginInterface {
|
||||
|
||||
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;
|
||||
|
@ -1,5 +1,5 @@
|
||||
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) {
|
||||
const plugins = [];
|
||||
@ -76,6 +76,10 @@ async function createDevPluginSymLink(pluginName) {
|
||||
}
|
||||
const link = resolve(nodeModulesPath, pluginName);
|
||||
if (await fsExists(link)) {
|
||||
const real = await realpath(link);
|
||||
if (real === resolve(packagePluginsPath, pluginName)) {
|
||||
return;
|
||||
}
|
||||
await unlink(link);
|
||||
}
|
||||
await symlink(resolve(packagePluginsPath, pluginName), link, 'dir');
|
||||
|
@ -7,9 +7,25 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
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) {
|
||||
if (typeof m === 'string') {
|
||||
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) {
|
||||
let packageName = name;
|
||||
try {
|
||||
packageName = await PluginManager.getPackageName(name);
|
||||
} catch (error) {
|
||||
packageName = name;
|
||||
}
|
||||
const { packageName } = await PluginManager.parseName(name);
|
||||
const packageJson = await PluginManager.getPackageJson(packageName);
|
||||
return packageJson;
|
||||
return { ...packageJson, name: packageName };
|
||||
}
|
||||
|
||||
async allPlugins() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user