mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-12 15:46:29 +08:00
chore(tools): add package selector to dev command (#9328)
This commit is contained in:
@@ -7,7 +7,7 @@ export class BuildCommand extends PackageCommand {
|
||||
const args: string[] = [];
|
||||
|
||||
if (this.deps) {
|
||||
args.push('--deps');
|
||||
args.push('--deps', '--wait-deps');
|
||||
}
|
||||
|
||||
args.push(this.package, 'build');
|
||||
|
||||
@@ -2,6 +2,7 @@ import { AliasToPackage } from '@affine-tools/utils/distribution';
|
||||
import { Logger } from '@affine-tools/utils/logger';
|
||||
import { type PackageName, Workspace } from '@affine-tools/utils/workspace';
|
||||
import { Command as BaseCommand, Option } from 'clipanion';
|
||||
import inquirer from 'inquirer';
|
||||
import * as t from 'typanion';
|
||||
|
||||
import type { CliContext } from './context';
|
||||
@@ -32,10 +33,14 @@ export abstract class PackageCommand extends Command {
|
||||
});
|
||||
|
||||
get package(): PackageName {
|
||||
return (
|
||||
const name =
|
||||
AliasToPackage.get(this.packageNameOrAlias as any) ??
|
||||
(this.packageNameOrAlias as PackageName)
|
||||
);
|
||||
(this.packageNameOrAlias as PackageName);
|
||||
|
||||
// check
|
||||
this.workspace.getPackage(name);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
protected _deps = Option.Boolean('--deps', false, {
|
||||
@@ -76,4 +81,50 @@ export abstract class PackagesCommand extends Command {
|
||||
});
|
||||
}
|
||||
|
||||
export abstract class PackageSelectorCommand extends Command {
|
||||
protected availablePackages = Workspace.PackageNames;
|
||||
|
||||
protected availablePackageNameArgs = (
|
||||
Workspace.PackageNames as string[]
|
||||
).concat(Array.from(AliasToPackage.keys()));
|
||||
|
||||
protected packageNameValidator = t.isOneOf(
|
||||
this.availablePackageNameArgs.map(k => t.isLiteral(k))
|
||||
);
|
||||
|
||||
protected packageNameOrAlias = Option.String('--package,-p', {
|
||||
validator: this.packageNameValidator,
|
||||
description: 'The package name or alias to be run with',
|
||||
});
|
||||
|
||||
async getPackage(): Promise<PackageName> {
|
||||
let name = this.packageNameOrAlias
|
||||
? (AliasToPackage.get(this.packageNameOrAlias as any) ??
|
||||
this.packageNameOrAlias)
|
||||
: undefined;
|
||||
|
||||
if (!name) {
|
||||
const answer = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'package',
|
||||
message: 'Which package do you want to dev?',
|
||||
choices: this.availablePackages.map(name => ({
|
||||
name,
|
||||
value: name,
|
||||
})),
|
||||
default: '@affine/web',
|
||||
},
|
||||
]);
|
||||
|
||||
name = answer.package as PackageName;
|
||||
}
|
||||
|
||||
// check
|
||||
this.workspace.getPackage(name as PackageName);
|
||||
|
||||
return name as PackageName;
|
||||
}
|
||||
}
|
||||
|
||||
export { Option };
|
||||
|
||||
+21
-4
@@ -1,16 +1,33 @@
|
||||
import { PackageCommand } from './command';
|
||||
import type { PackageName } from '@affine-tools/utils/workspace';
|
||||
|
||||
export class DevCommand extends PackageCommand {
|
||||
import { Option, PackageSelectorCommand } from './command';
|
||||
|
||||
export class DevCommand extends PackageSelectorCommand {
|
||||
static override paths = [['dev'], ['d']];
|
||||
|
||||
protected override availablePackages: PackageName[] = [
|
||||
'@affine/web',
|
||||
'@affine/server',
|
||||
'@affine/electron',
|
||||
'@affine/electron-renderer',
|
||||
'@affine/mobile',
|
||||
'@affine/ios',
|
||||
'@affine/android',
|
||||
];
|
||||
|
||||
protected deps = Option.Boolean('--deps', {
|
||||
description: 'Run dev with dependencies',
|
||||
});
|
||||
|
||||
async execute() {
|
||||
const name = await this.getPackage();
|
||||
const args = [];
|
||||
|
||||
if (this.deps) {
|
||||
args.push('--deps', '--wait-deps');
|
||||
args.push('--deps');
|
||||
}
|
||||
|
||||
args.push(this.package, 'dev');
|
||||
args.push(name, 'dev');
|
||||
|
||||
await this.cli.run(args);
|
||||
}
|
||||
|
||||
+86
-26
@@ -19,6 +19,7 @@ const ignoreLoaderScripts = [
|
||||
'prisma',
|
||||
'cap',
|
||||
'tsc',
|
||||
/^r$/,
|
||||
/electron(?!-)/,
|
||||
];
|
||||
|
||||
@@ -30,7 +31,7 @@ export class RunCommand extends PackageCommand {
|
||||
details: `
|
||||
\`affine web <script>\` Run any script defined in package's package.json
|
||||
|
||||
\`affine codegen\` Generate the required files if there are any package added or removed
|
||||
\`affine init\` Generate the required files if there are any package added or removed
|
||||
|
||||
\`affine clean\` Clean the output files of ts, cargo, webpack, etc.
|
||||
|
||||
@@ -88,7 +89,7 @@ export class RunCommand extends PackageCommand {
|
||||
if (pkgScript) {
|
||||
await this.runScript(pkg, scriptName, args.slice(1), opts);
|
||||
} else {
|
||||
await this.runCommand(pkg, scriptName, args.slice(1));
|
||||
await this.runCommand(pkg, args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,9 +99,9 @@ export class RunCommand extends PackageCommand {
|
||||
args: string[],
|
||||
opts: RunScriptOptions = {}
|
||||
) {
|
||||
const script = pkg.scripts[scriptName];
|
||||
const rawScript = pkg.scripts[scriptName];
|
||||
|
||||
if (!script) {
|
||||
if (!rawScript) {
|
||||
if (opts.ignoreIfNotFound) {
|
||||
return;
|
||||
}
|
||||
@@ -108,14 +109,19 @@ export class RunCommand extends PackageCommand {
|
||||
throw new Error(`Script ${scriptName} not found in ${pkg.name}`);
|
||||
}
|
||||
|
||||
const isAFFiNECommand = script.startsWith('affine ');
|
||||
const rawArgs = [...rawScript.split(' '), ...args];
|
||||
|
||||
const { args: extractedArgs, envs } = this.extractEnvs(rawArgs);
|
||||
|
||||
args = extractedArgs;
|
||||
|
||||
if (opts.includeDependencies) {
|
||||
const depsRun = Promise.all(
|
||||
pkg.deps.map(dep => {
|
||||
return this.runScript(
|
||||
pkg.workspace.getPackage(dep.name),
|
||||
scriptName,
|
||||
args,
|
||||
[],
|
||||
{
|
||||
...opts,
|
||||
ignoreIfNotFound: true,
|
||||
@@ -123,6 +129,7 @@ export class RunCommand extends PackageCommand {
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
if (opts.waitDependencies) {
|
||||
await depsRun;
|
||||
} else {
|
||||
@@ -132,34 +139,87 @@ export class RunCommand extends PackageCommand {
|
||||
}
|
||||
}
|
||||
|
||||
args = [...script.split(' '), ...args];
|
||||
|
||||
const isAFFiNECommand = args[0] === 'affine';
|
||||
if (isAFFiNECommand) {
|
||||
// remove 'affine' from 'affine xxx' command
|
||||
args.shift();
|
||||
args.push('-p', pkg.name);
|
||||
|
||||
process.env = {
|
||||
...process.env,
|
||||
...envs,
|
||||
};
|
||||
await this.cli.run(args);
|
||||
} else {
|
||||
args.unshift(pkg.name);
|
||||
await this.runCommand(pkg, rawArgs);
|
||||
}
|
||||
}
|
||||
|
||||
async runCommand(pkg: Package, args: string[]) {
|
||||
const { args: extractedArgs, envs } = this.extractEnvs(args);
|
||||
args = extractedArgs;
|
||||
|
||||
const bin = args[0] === 'yarn' ? args[1] : args[0];
|
||||
|
||||
const loader = currentDir.join('../register.js').toFileUrl().toString();
|
||||
|
||||
// very simple test for auto ts/mjs scripts
|
||||
const isLoaderRequired =
|
||||
!ignoreLoaderScripts.some(ignore => new RegExp(ignore).test(bin)) ||
|
||||
process.env.NODE_OPTIONS?.includes('ts-node/esm') ||
|
||||
process.env.NODE_OPTIONS?.includes(loader);
|
||||
|
||||
let NODE_OPTIONS = process.env.NODE_OPTIONS
|
||||
? [process.env.NODE_OPTIONS]
|
||||
: [];
|
||||
|
||||
if (isLoaderRequired) {
|
||||
NODE_OPTIONS.push(`--import=${loader}`);
|
||||
}
|
||||
|
||||
await this.cli.run(args);
|
||||
}
|
||||
if (args[0] !== 'yarn') {
|
||||
// add 'yarn' to the command so we can bypass bin execution to it
|
||||
args.unshift('yarn');
|
||||
}
|
||||
|
||||
async runCommand(pkg: Package, scriptName: string, args: string[]) {
|
||||
// very simple test for auto ts/mjs scripts
|
||||
// TODO(@forehalo): bypass cross-env and fetch the next script after envs
|
||||
const isLoaderRequired = !ignoreLoaderScripts.some(ignore =>
|
||||
new RegExp(ignore).test(scriptName)
|
||||
);
|
||||
|
||||
await execAsync(pkg.name, ['yarn', scriptName, ...args], {
|
||||
await execAsync(pkg.name, args, {
|
||||
cwd: pkg.path.value,
|
||||
...(isLoaderRequired
|
||||
? {
|
||||
env: {
|
||||
NODE_OPTIONS: `--import=${currentDir.join('../register.js').toFileUrl()}`,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
env: {
|
||||
...envs,
|
||||
NODE_OPTIONS: NODE_OPTIONS.join(' '),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private extractEnvs(args: string[]): {
|
||||
args: string[];
|
||||
envs: Record<string, string>;
|
||||
} {
|
||||
const envs: Record<string, string> = {};
|
||||
|
||||
let i = 0;
|
||||
|
||||
while (i < args.length) {
|
||||
const arg = args[i];
|
||||
if (arg === 'cross-env') {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const match = arg.match(/^([A-Z_]+)=(.+)$/);
|
||||
|
||||
if (match) {
|
||||
envs[match[1]] = match[2];
|
||||
i++;
|
||||
} else {
|
||||
// not envs any more
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
args: args.slice(i),
|
||||
envs,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,7 +316,6 @@ export function createWebpackConfig(
|
||||
// copy the shared public assets into dist
|
||||
from: pkg.workspace.getPackage('@affine/core').join('public')
|
||||
.value,
|
||||
to: pkg.distPath.value,
|
||||
},
|
||||
],
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user