mirror of
https://github.com/strapi/strapi.git
synced 2025-11-02 10:55:37 +00:00
Fix Upgrade Tool Targeted Files For Plugins (#21033)
This commit is contained in:
parent
45c8d25668
commit
ca3cb5d50a
@ -3,7 +3,7 @@ import chalk from 'chalk';
|
||||
|
||||
import { constants as timerConstants } from '../timer';
|
||||
|
||||
import type { ProjectType } from '../project';
|
||||
import type { AppProject, PluginProject, ProjectType } from '../project';
|
||||
import type { Codemod } from '../codemod';
|
||||
import type { Version } from '../version';
|
||||
import type { Report } from '../report';
|
||||
@ -18,6 +18,10 @@ export const codemodUID = (uid: string) => {
|
||||
return chalk.bold.cyan(uid);
|
||||
};
|
||||
|
||||
export const projectDetails = (project: AppProject | PluginProject) => {
|
||||
return `Project: TYPE=${projectType(project.type)}; CWD=${path(project.cwd)}; PATHS=${project.paths.map(path)}`;
|
||||
};
|
||||
|
||||
export const projectType = (type: ProjectType) => chalk.cyan(type);
|
||||
|
||||
export const versionRange = (range: Version.Range) => chalk.italic.yellow(range.raw);
|
||||
|
||||
@ -8,7 +8,12 @@ jest.mock('fs', () => fs);
|
||||
|
||||
const srcFilename = (cwd: string, filename: string) => path.join(cwd, 'src', filename);
|
||||
const srcFilenames = (cwd: string) => {
|
||||
return Object.keys(srcFiles).map((filename) => srcFilename(cwd, filename));
|
||||
return Object.keys(defaultFiles).map((filename) => srcFilename(cwd, filename));
|
||||
};
|
||||
|
||||
const pluginServerFilename = (cwd: string, filename: string) => path.join(cwd, 'server', filename);
|
||||
const pluginServerFilenames = (cwd: string) => {
|
||||
return Object.keys(defaultFiles).map((filename) => pluginServerFilename(cwd, filename));
|
||||
};
|
||||
|
||||
const currentStrapiVersion = '1.2.3';
|
||||
@ -29,7 +34,7 @@ const pluginPackageJSONFile = `{
|
||||
}
|
||||
}`;
|
||||
|
||||
const srcFiles = {
|
||||
const defaultFiles = {
|
||||
'a.ts': 'console.log("a.ts")',
|
||||
'b.ts': 'console.log("b.ts")',
|
||||
'c.js': 'console.log("c.js")',
|
||||
@ -40,12 +45,12 @@ const srcFiles = {
|
||||
|
||||
const appVolume = {
|
||||
'package.json': appPackageJSONFile,
|
||||
src: srcFiles,
|
||||
src: defaultFiles,
|
||||
};
|
||||
|
||||
const pluginVolume = {
|
||||
'package.json': pluginPackageJSONFile,
|
||||
src: srcFiles,
|
||||
server: defaultFiles,
|
||||
};
|
||||
|
||||
describe('Project', () => {
|
||||
@ -70,7 +75,7 @@ describe('Project', () => {
|
||||
});
|
||||
|
||||
test('Fails on project without package.json file', async () => {
|
||||
vol.fromNestedJSON({ src: srcFiles }, defaultCWD);
|
||||
vol.fromNestedJSON({ src: defaultFiles }, defaultCWD);
|
||||
|
||||
expect(() => projectFactory(defaultCWD)).toThrow(
|
||||
`Could not find a package.json file in ${defaultCWD}`
|
||||
@ -79,7 +84,7 @@ describe('Project', () => {
|
||||
|
||||
test('Fails when not a plugin and no @strapi/strapi dependency found', async () => {
|
||||
vol.fromNestedJSON(
|
||||
{ 'package.json': `{ "name": "test", "version": "1.2.3" }`, src: srcFiles },
|
||||
{ 'package.json': `{ "name": "test", "version": "1.2.3" }`, src: defaultFiles },
|
||||
defaultCWD
|
||||
);
|
||||
|
||||
@ -92,7 +97,7 @@ describe('Project', () => {
|
||||
vol.fromNestedJSON(
|
||||
{
|
||||
'package.json': `{ "name": "test", "version": "1.2.3", "dependencies": { "@strapi/strapi": "^4.0.0" } }`,
|
||||
src: srcFiles,
|
||||
src: defaultFiles,
|
||||
},
|
||||
defaultCWD
|
||||
);
|
||||
@ -133,7 +138,10 @@ describe('Project', () => {
|
||||
|
||||
expect(project.files.length).toBe(7);
|
||||
expect(project.files).toStrictEqual(
|
||||
expect.arrayContaining([path.join(defaultCWD, 'package.json'), ...srcFilenames(defaultCWD)])
|
||||
expect.arrayContaining([
|
||||
path.join(defaultCWD, 'package.json'),
|
||||
...pluginServerFilenames(defaultCWD),
|
||||
])
|
||||
);
|
||||
|
||||
expect(project.cwd).toBe(defaultCWD);
|
||||
@ -172,7 +180,10 @@ describe('Project', () => {
|
||||
|
||||
expect(project.files.length).toBe(7);
|
||||
expect(project.files).toStrictEqual(
|
||||
expect.arrayContaining([path.join(defaultCWD, 'package.json'), ...srcFilenames(defaultCWD)])
|
||||
expect.arrayContaining([
|
||||
path.join(defaultCWD, 'package.json'),
|
||||
...pluginServerFilenames(defaultCWD),
|
||||
])
|
||||
);
|
||||
|
||||
expect(project.cwd).toBe(defaultCWD);
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
export const PROJECT_PACKAGE_JSON = 'package.json';
|
||||
|
||||
export const PROJECT_DEFAULT_ALLOWED_ROOT_PATHS = ['src', 'config', 'public', 'admin', 'server'];
|
||||
export const PROJECT_APP_ALLOWED_ROOT_PATHS = ['src', 'config', 'public'];
|
||||
|
||||
export const PROJECT_DEFAULT_CODE_EXTENSIONS = [
|
||||
export const PROJECT_PLUGIN_ALLOWED_ROOT_PATHS = ['admin', 'server'];
|
||||
|
||||
export const PROJECT_PLUGIN_ROOT_FILES = ['strapi-admin.js', 'strapi-server.js'];
|
||||
|
||||
export const PROJECT_CODE_EXTENSIONS = [
|
||||
// Source files
|
||||
'js',
|
||||
'mjs',
|
||||
@ -12,14 +16,9 @@ export const PROJECT_DEFAULT_CODE_EXTENSIONS = [
|
||||
'tsx',
|
||||
];
|
||||
|
||||
export const PROJECT_DEFAULT_JSON_EXTENSIONS = ['json'];
|
||||
export const PROJECT_JSON_EXTENSIONS = ['json'];
|
||||
|
||||
export const PROJECT_DEFAULT_ALLOWED_EXTENSIONS = [
|
||||
...PROJECT_DEFAULT_CODE_EXTENSIONS,
|
||||
...PROJECT_DEFAULT_JSON_EXTENSIONS,
|
||||
];
|
||||
|
||||
export const PROJECT_DEFAULT_PATTERNS = ['package.json'];
|
||||
export const PROJECT_ALLOWED_EXTENSIONS = [...PROJECT_CODE_EXTENSIONS, ...PROJECT_JSON_EXTENSIONS];
|
||||
|
||||
export const SCOPED_STRAPI_PACKAGE_PREFIX = '@strapi/';
|
||||
|
||||
|
||||
@ -12,7 +12,13 @@ import * as constants from './constants';
|
||||
import type { Version } from '../version';
|
||||
import type { Codemod } from '../codemod';
|
||||
import type { Report } from '../report';
|
||||
import type { FileExtension, MinimalPackageJSON, ProjectType, RunCodemodsOptions } from './types';
|
||||
import type {
|
||||
FileExtension,
|
||||
MinimalPackageJSON,
|
||||
ProjectConfig,
|
||||
ProjectType,
|
||||
RunCodemodsOptions,
|
||||
} from './types';
|
||||
|
||||
export class Project {
|
||||
public cwd: string;
|
||||
@ -25,12 +31,15 @@ export class Project {
|
||||
|
||||
public packageJSON!: MinimalPackageJSON;
|
||||
|
||||
constructor(cwd: string) {
|
||||
public readonly paths: string[];
|
||||
|
||||
constructor(cwd: string, config: ProjectConfig) {
|
||||
if (!fse.pathExistsSync(cwd)) {
|
||||
throw new Error(`ENOENT: no such file or directory, access '${cwd}'`);
|
||||
}
|
||||
|
||||
this.cwd = cwd;
|
||||
this.paths = config.paths;
|
||||
|
||||
this.refresh();
|
||||
}
|
||||
@ -67,12 +76,8 @@ export class Project {
|
||||
}
|
||||
|
||||
private createProjectCodemodsRunners(dry: boolean = false) {
|
||||
const jsonExtensions = constants.PROJECT_DEFAULT_JSON_EXTENSIONS.map<FileExtension>(
|
||||
(ext) => `.${ext}`
|
||||
);
|
||||
const codeExtensions = constants.PROJECT_DEFAULT_CODE_EXTENSIONS.map<FileExtension>(
|
||||
(ext) => `.${ext}`
|
||||
);
|
||||
const jsonExtensions = constants.PROJECT_JSON_EXTENSIONS.map<FileExtension>((ext) => `.${ext}`);
|
||||
const codeExtensions = constants.PROJECT_CODE_EXTENSIONS.map<FileExtension>((ext) => `.${ext}`);
|
||||
|
||||
const jsonFiles = this.getFilesByExtensions(jsonExtensions);
|
||||
const codeFiles = this.getFilesByExtensions(codeExtensions);
|
||||
@ -82,7 +87,7 @@ export class Project {
|
||||
parser: 'ts',
|
||||
runInBand: true,
|
||||
babel: true,
|
||||
extensions: constants.PROJECT_DEFAULT_CODE_EXTENSIONS.join(','),
|
||||
extensions: constants.PROJECT_CODE_EXTENSIONS.join(','),
|
||||
// Don't output any log coming from the runner
|
||||
print: false,
|
||||
silent: true,
|
||||
@ -109,20 +114,9 @@ export class Project {
|
||||
}
|
||||
|
||||
private refreshProjectFiles(): void {
|
||||
const allowedRootPaths = formatGlobCollectionPattern(
|
||||
constants.PROJECT_DEFAULT_ALLOWED_ROOT_PATHS
|
||||
);
|
||||
|
||||
const allowedExtensions = formatGlobCollectionPattern(
|
||||
constants.PROJECT_DEFAULT_ALLOWED_EXTENSIONS
|
||||
);
|
||||
|
||||
const projectFilesPattern = `./${allowedRootPaths}/**/*.${allowedExtensions}`;
|
||||
|
||||
const patterns = [projectFilesPattern, ...constants.PROJECT_DEFAULT_PATTERNS];
|
||||
const scanner = fileScannerFactory(this.cwd);
|
||||
|
||||
this.files = scanner.scan(patterns);
|
||||
this.files = scanner.scan(this.paths);
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,8 +125,25 @@ export class AppProject extends Project {
|
||||
|
||||
readonly type = 'application' as const satisfies ProjectType;
|
||||
|
||||
/**
|
||||
* Returns an array of allowed file paths for a Strapi application
|
||||
*
|
||||
* The resulting paths include app default files and the root package.json file.
|
||||
*/
|
||||
private static get paths() {
|
||||
const allowedRootPaths = formatGlobCollectionPattern(constants.PROJECT_APP_ALLOWED_ROOT_PATHS);
|
||||
const allowedExtensions = formatGlobCollectionPattern(constants.PROJECT_ALLOWED_EXTENSIONS);
|
||||
|
||||
return [
|
||||
// App default files
|
||||
`./${allowedRootPaths}/**/*.${allowedExtensions}`,
|
||||
// Root package.json file
|
||||
constants.PROJECT_PACKAGE_JSON,
|
||||
];
|
||||
}
|
||||
|
||||
constructor(cwd: string) {
|
||||
super(cwd);
|
||||
super(cwd, { paths: AppProject.paths });
|
||||
this.refreshStrapiVersion();
|
||||
}
|
||||
|
||||
@ -206,6 +217,31 @@ const formatGlobCollectionPattern = (collection: string[]): string => {
|
||||
|
||||
export class PluginProject extends Project {
|
||||
readonly type = 'plugin' as const satisfies ProjectType;
|
||||
|
||||
/**
|
||||
* Returns an array of allowed file paths for a Strapi plugin
|
||||
*
|
||||
* The resulting paths include plugin default files, the root package.json file, and plugin-specific files.
|
||||
*/
|
||||
private static get paths() {
|
||||
const allowedRootPaths = formatGlobCollectionPattern(
|
||||
constants.PROJECT_PLUGIN_ALLOWED_ROOT_PATHS
|
||||
);
|
||||
const allowedExtensions = formatGlobCollectionPattern(constants.PROJECT_ALLOWED_EXTENSIONS);
|
||||
|
||||
return [
|
||||
// Plugin default files
|
||||
`./${allowedRootPaths}/**/*.${allowedExtensions}`,
|
||||
// Root package.json file
|
||||
constants.PROJECT_PACKAGE_JSON,
|
||||
// Plugin root files
|
||||
...constants.PROJECT_PLUGIN_ROOT_FILES,
|
||||
];
|
||||
}
|
||||
|
||||
constructor(cwd: string) {
|
||||
super(cwd, { paths: PluginProject.paths });
|
||||
}
|
||||
}
|
||||
|
||||
const isPlugin = (cwd: string) => {
|
||||
@ -228,9 +264,5 @@ const isPlugin = (cwd: string) => {
|
||||
export const projectFactory = (cwd: string) => {
|
||||
fse.accessSync(cwd);
|
||||
|
||||
if (isPlugin(cwd)) {
|
||||
return new PluginProject(cwd);
|
||||
}
|
||||
|
||||
return new AppProject(cwd);
|
||||
return isPlugin(cwd) ? new PluginProject(cwd) : new AppProject(cwd);
|
||||
};
|
||||
|
||||
@ -22,3 +22,7 @@ export type MinimalPackageJSON = {
|
||||
version: string;
|
||||
dependencies?: Record<string, string>;
|
||||
} & Utils.JSONObject;
|
||||
|
||||
export interface ProjectConfig {
|
||||
paths: string[];
|
||||
}
|
||||
|
||||
@ -43,6 +43,7 @@ describe('codemods task', () => {
|
||||
});
|
||||
|
||||
(projectFactory as jest.Mock).mockReturnValue({
|
||||
paths: [],
|
||||
dry: jest.fn().mockReturnThis(),
|
||||
onSelectCodemods: jest.fn().mockReturnThis(),
|
||||
setLogger: jest.fn().mockReturnThis(),
|
||||
|
||||
@ -13,7 +13,7 @@ export const listCodemods = async (options: ListCodemodsOptions) => {
|
||||
const project = projectFactory(cwd);
|
||||
const range = findRangeFromTarget(project, target);
|
||||
|
||||
logger.debug(`Project: ${f.projectType(project.type)} found in ${f.path(cwd)}`);
|
||||
logger.debug(f.projectDetails(project));
|
||||
logger.debug(`Range: set to ${f.versionRange(range)}`);
|
||||
|
||||
// Create a codemod repository targeting the default location of the codemods
|
||||
|
||||
@ -17,7 +17,7 @@ export const runCodemods = async (options: RunCodemodsOptions) => {
|
||||
const project = projectFactory(cwd);
|
||||
const range = findRangeFromTarget(project, options.target);
|
||||
|
||||
logger.debug(`Project: ${f.projectType(project.type)} found in ${f.path(cwd)}`);
|
||||
logger.debug(f.projectDetails(project));
|
||||
logger.debug(`Range: set to ${f.versionRange(range)}`);
|
||||
|
||||
const codemodRunner = codemodRunnerFactory(project, range)
|
||||
|
||||
@ -19,12 +19,16 @@ export const upgrade = async (options: UpgradeOptions) => {
|
||||
|
||||
const project = projectFactory(cwd);
|
||||
|
||||
logger.debug(f.projectDetails(project));
|
||||
|
||||
if (!isApplicationProject(project)) {
|
||||
throw new Error(
|
||||
`The "${options.target}" upgrade can only be run on a Strapi project; for plugins, please use "codemods".`
|
||||
);
|
||||
}
|
||||
|
||||
const npmPackage = npmPackageFactory(upgraderConstants.STRAPI_PACKAGE_NAME);
|
||||
|
||||
// Load all versions from the registry
|
||||
await npmPackage.refresh();
|
||||
|
||||
@ -46,11 +50,11 @@ export const upgrade = async (options: UpgradeOptions) => {
|
||||
.addRequirement(requirements.major.REQUIRE_LATEST_FOR_CURRENT_MAJOR);
|
||||
}
|
||||
|
||||
// Make sure the git repository is in an optimal state before running the upgrade
|
||||
// Make sure the git repository is in an optional state before running the upgrade
|
||||
// Mainly used to ease rollbacks in case the upgrade is corrupted
|
||||
upgrader.addRequirement(requirements.common.REQUIRE_GIT.asOptional());
|
||||
|
||||
// Actually run the upgrade process once configured
|
||||
// Actually run the upgrade process once configured,
|
||||
// The response contains information about the final status (success/error)
|
||||
const upgradeReport = await upgrader.upgrade();
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user