@strapi/strapi 4.13.5 → 4.13.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -29
- package/lib/commands/actions/develop/action.js +12 -17
- package/lib/commands/actions/plugin/build-command/action.js +137 -0
- package/lib/commands/actions/plugin/build-command/command.js +17 -0
- package/lib/commands/actions/ts/generate-types/action.js +2 -2
- package/lib/commands/builders/packages.js +252 -0
- package/lib/commands/builders/tasks/dts.js +199 -0
- package/lib/commands/builders/tasks/index.js +29 -0
- package/lib/commands/builders/tasks/vite.js +144 -0
- package/lib/commands/builders/typescript.js +2 -2
- package/lib/commands/index.js +1 -0
- package/lib/commands/utils/helpers.js +53 -1
- package/lib/commands/utils/logger.js +97 -0
- package/lib/commands/utils/pkg.js +421 -0
- package/lib/compile.d.ts +13 -0
- package/lib/compile.js +8 -4
- package/lib/index.d.ts +1 -0
- package/lib/services/entity-service/types/params/filters/operators.d.ts +1 -0
- package/lib/types/core/strapi/index.d.ts +2 -2
- package/package.json +22 -17
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const ora = require('ora');
|
|
6
|
+
const ts = require('typescript');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @description Load a tsconfig.json file and return the parsed config
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
*
|
|
13
|
+
* @type {(args: { cwd: string; path: string }) => Promise<ts.ParsedCommandLine>)}
|
|
14
|
+
*/
|
|
15
|
+
const loadTsConfig = async ({ cwd, path }) => {
|
|
16
|
+
const configPath = ts.findConfigFile(cwd, ts.sys.fileExists, path);
|
|
17
|
+
|
|
18
|
+
if (!configPath) {
|
|
19
|
+
throw new TSConfigNotFoundError(`could not find a valid '${path}'`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
23
|
+
|
|
24
|
+
return ts.parseJsonConfigFileContent(configFile.config, ts.sys, cwd);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
class TSConfigNotFoundError extends Error {
|
|
28
|
+
// eslint-disable-next-line no-useless-constructor
|
|
29
|
+
constructor(message, options) {
|
|
30
|
+
super(message, options);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get code() {
|
|
34
|
+
return 'TS_CONFIG_NOT_FOUND';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @description
|
|
40
|
+
*
|
|
41
|
+
* @internal
|
|
42
|
+
*
|
|
43
|
+
* @type {(args: { cwd: string; logger: import('../../utils/logger').Logger; outDir: string; tsconfig: ts.ParsedCommandLine }) => Promise<void>}
|
|
44
|
+
*/
|
|
45
|
+
const buildTypes = ({ cwd, logger, outDir, tsconfig }) => {
|
|
46
|
+
const compilerOptions = {
|
|
47
|
+
...tsconfig.options,
|
|
48
|
+
declaration: true,
|
|
49
|
+
declarationDir: outDir,
|
|
50
|
+
emitDeclarationOnly: true,
|
|
51
|
+
noEmit: false,
|
|
52
|
+
outDir,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const program = ts.createProgram(tsconfig.fileNames, compilerOptions);
|
|
56
|
+
|
|
57
|
+
const emitResult = program.emit();
|
|
58
|
+
|
|
59
|
+
const allDiagnostics = ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics);
|
|
60
|
+
|
|
61
|
+
for (const diagnostic of allDiagnostics) {
|
|
62
|
+
if (diagnostic.file && diagnostic.start) {
|
|
63
|
+
const { line, character } = ts.getLineAndCharacterOfPosition(
|
|
64
|
+
diagnostic.file,
|
|
65
|
+
diagnostic.start
|
|
66
|
+
);
|
|
67
|
+
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
|
|
68
|
+
|
|
69
|
+
const file = path.relative(cwd, diagnostic.file.fileName);
|
|
70
|
+
|
|
71
|
+
const output = [
|
|
72
|
+
`${chalk.cyan(file)}:${chalk.cyan(line + 1)}:${chalk.cyan(character + 1)} - `,
|
|
73
|
+
`${chalk.gray(`TS${diagnostic.code}:`)} ${message}`,
|
|
74
|
+
].join('');
|
|
75
|
+
|
|
76
|
+
if (diagnostic.category === ts.DiagnosticCategory.Error) {
|
|
77
|
+
logger.error(output);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (diagnostic.category === ts.DiagnosticCategory.Warning) {
|
|
81
|
+
logger.warn(output);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (diagnostic.category === ts.DiagnosticCategory.Message) {
|
|
85
|
+
logger.info(output);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (diagnostic.category === ts.DiagnosticCategory.Suggestion) {
|
|
89
|
+
logger.info(output);
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
logger.info(ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (emitResult.emitSkipped) {
|
|
97
|
+
const errors = allDiagnostics.filter((diag) => diag.category === ts.DiagnosticCategory.Error);
|
|
98
|
+
|
|
99
|
+
if (errors.length) {
|
|
100
|
+
throw new Error('Failed to compile TypeScript definitions');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @typedef {Object} DtsTaskEntry
|
|
107
|
+
* @property {string} exportPath
|
|
108
|
+
* @property {string} sourcePath
|
|
109
|
+
* @property {string} targetPath
|
|
110
|
+
*/
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @typedef {Object} DtsTask
|
|
114
|
+
* @property {"build:dts"} type
|
|
115
|
+
* @property {DtsTaskEntry[]} entries
|
|
116
|
+
*/
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @type {import('./index').TaskHandler<DtsTask>}
|
|
120
|
+
*/
|
|
121
|
+
const dtsTask = {
|
|
122
|
+
_spinner: null,
|
|
123
|
+
print(ctx, task) {
|
|
124
|
+
const entries = [
|
|
125
|
+
' entries:',
|
|
126
|
+
...task.entries.map((entry) =>
|
|
127
|
+
[
|
|
128
|
+
` – `,
|
|
129
|
+
chalk.green(`${entry.importId} `),
|
|
130
|
+
`${chalk.cyan(entry.sourcePath)} ${chalk.gray('→')} ${chalk.cyan(entry.targetPath)}`,
|
|
131
|
+
].join('')
|
|
132
|
+
),
|
|
133
|
+
'',
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
this._spinner = ora(`Building type files:\n`).start();
|
|
137
|
+
|
|
138
|
+
ctx.logger.log([...entries].join('\n'));
|
|
139
|
+
},
|
|
140
|
+
async run(ctx, task) {
|
|
141
|
+
try {
|
|
142
|
+
await Promise.all(
|
|
143
|
+
task.entries.map(async (entry) => {
|
|
144
|
+
const config = await loadTsConfig({
|
|
145
|
+
/**
|
|
146
|
+
* TODO: this will not scale and assumes all project sourcePaths are `src/index.ts`
|
|
147
|
+
* so we can go back to the "root" of the project...
|
|
148
|
+
*/
|
|
149
|
+
cwd: path.join(ctx.cwd, entry.sourcePath, '..', '..'),
|
|
150
|
+
path: 'tsconfig.build.json',
|
|
151
|
+
}).catch((err) => {
|
|
152
|
+
if (err instanceof TSConfigNotFoundError) {
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
throw err;
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
if (config) {
|
|
160
|
+
ctx.logger.debug(`TS config for '${entry.sourcePath}': \n`, config);
|
|
161
|
+
} else {
|
|
162
|
+
ctx.logger.warn(
|
|
163
|
+
`You've added a types entry but no tsconfig.json was found for ${entry.targetPath}. Skipping...`
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const { outDir } = config.raw.compilerOptions;
|
|
170
|
+
|
|
171
|
+
if (!outDir) {
|
|
172
|
+
throw new Error("tsconfig.json is missing 'compilerOptions.outDir'");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
await buildTypes({
|
|
176
|
+
cwd: ctx.cwd,
|
|
177
|
+
logger: ctx.logger,
|
|
178
|
+
outDir: path.relative(ctx.cwd, outDir),
|
|
179
|
+
tsconfig: config,
|
|
180
|
+
});
|
|
181
|
+
})
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
await this.success(ctx, task);
|
|
185
|
+
} catch (err) {
|
|
186
|
+
this.fail(ctx, task, err);
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
async success() {
|
|
190
|
+
this._spinner.succeed('Built type files');
|
|
191
|
+
},
|
|
192
|
+
async fail(ctx, task, err) {
|
|
193
|
+
this._spinner.fail('Failed to build type files');
|
|
194
|
+
|
|
195
|
+
throw err;
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
module.exports = { dtsTask };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { dtsTask } = require('./dts');
|
|
4
|
+
const { viteTask } = require('./vite');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @template Task
|
|
8
|
+
* @param {Task}
|
|
9
|
+
* @returns {Task}
|
|
10
|
+
*
|
|
11
|
+
* @typedef {Object} TaskHandler
|
|
12
|
+
* @property {(ctx: import("../packages").BuildContext, task: Task) => import('ora').Ora} print
|
|
13
|
+
* @property {(ctx: import("../packages").BuildContext, task: Task) => Promise<void>} run
|
|
14
|
+
* @property {(ctx: import("../packages").BuildContext, task: Task) => Promise<void>} success
|
|
15
|
+
* @property {(ctx: import("../packages").BuildContext, task: Task, err: unknown) => Promise<void>} fail
|
|
16
|
+
* @property {import('ora').Ora | null} _spinner
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @type {{ "build:js": TaskHandler<import("./vite").ViteTask>; "build:dts": TaskHandler<import("./dts").DtsTask>; }}}
|
|
21
|
+
*/
|
|
22
|
+
const buildTaskHandlers = {
|
|
23
|
+
'build:js': viteTask,
|
|
24
|
+
'build:dts': dtsTask,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
module.exports = {
|
|
28
|
+
buildTaskHandlers,
|
|
29
|
+
};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { build, createLogger } = require('vite');
|
|
5
|
+
const react = require('@vitejs/plugin-react');
|
|
6
|
+
const ora = require('ora');
|
|
7
|
+
const chalk = require('chalk');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @internal
|
|
11
|
+
*
|
|
12
|
+
* @type {(ctx: import('../packages').BuildContext, task: ViteTask) => import('vite').UserConfig}
|
|
13
|
+
*/
|
|
14
|
+
const resolveViteConfig = (ctx, task) => {
|
|
15
|
+
const { cwd, distPath, targets, external, extMap, pkg } = ctx;
|
|
16
|
+
const { entries, format, output, runtime } = task;
|
|
17
|
+
const outputExt = extMap[pkg.type || 'commonjs'][format];
|
|
18
|
+
const outDir = path.relative(cwd, distPath);
|
|
19
|
+
|
|
20
|
+
const customLogger = createLogger();
|
|
21
|
+
customLogger.warn = (msg) => ctx.logger.warn(msg);
|
|
22
|
+
customLogger.warnOnce = (msg) => ctx.logger.warn(msg);
|
|
23
|
+
customLogger.error = (msg) => ctx.logger.error(msg);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @type {import('vite').InlineConfig}
|
|
27
|
+
*/
|
|
28
|
+
const config = {
|
|
29
|
+
configFile: false,
|
|
30
|
+
root: cwd,
|
|
31
|
+
mode: 'production',
|
|
32
|
+
logLevel: 'warn',
|
|
33
|
+
clearScreen: false,
|
|
34
|
+
customLogger,
|
|
35
|
+
build: {
|
|
36
|
+
sourcemap: true,
|
|
37
|
+
/**
|
|
38
|
+
* The task runner will clear this for us
|
|
39
|
+
*/
|
|
40
|
+
emptyOutDir: false,
|
|
41
|
+
target: targets[runtime],
|
|
42
|
+
outDir,
|
|
43
|
+
lib: {
|
|
44
|
+
entry: entries.map((e) => e.entry),
|
|
45
|
+
formats: [format],
|
|
46
|
+
/**
|
|
47
|
+
* this enforces the file name to match what the output we've
|
|
48
|
+
* determined from the package.json exports.
|
|
49
|
+
*/
|
|
50
|
+
fileName() {
|
|
51
|
+
return `${path.relative(outDir, output).replace(/\.[^/.]+$/, '')}${outputExt}`;
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
rollupOptions: {
|
|
55
|
+
external,
|
|
56
|
+
output: {
|
|
57
|
+
chunkFileNames() {
|
|
58
|
+
const parts = outputExt.split('.');
|
|
59
|
+
|
|
60
|
+
if (parts.length === 3) {
|
|
61
|
+
return `_chunks/[name]-[hash].${parts[2]}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return `_chunks/[name]-[hash]${outputExt}`;
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
/**
|
|
70
|
+
* We _could_ omit this, but we'd need to introduce the
|
|
71
|
+
* concept of a custom config for the scripts straight away
|
|
72
|
+
*
|
|
73
|
+
* and since this is isolated to the Strapi CLI, we can make
|
|
74
|
+
* some assumptions and add some weight until we move it outside.
|
|
75
|
+
*/
|
|
76
|
+
plugins: runtime === 'node' ? [] : [react()],
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
return config;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @typedef {Object} ViteTaskEntry
|
|
84
|
+
* @property {string} path
|
|
85
|
+
* @property {string} entry
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @typedef {Object} ViteTask
|
|
90
|
+
* @property {"build:js"} type
|
|
91
|
+
* @property {ViteTaskEntry[]} entries
|
|
92
|
+
* @property {string} format
|
|
93
|
+
* @property {string} output
|
|
94
|
+
* @property {keyof import('../packages').Targets} runtime
|
|
95
|
+
*/
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @type {import('./index').TaskHandler<ViteTask>}
|
|
99
|
+
*/
|
|
100
|
+
const viteTask = {
|
|
101
|
+
_spinner: null,
|
|
102
|
+
print(ctx, task) {
|
|
103
|
+
const targetLines = [
|
|
104
|
+
' target:',
|
|
105
|
+
...ctx.targets[task.runtime].map((t) => chalk.cyan(` - ${t}`)),
|
|
106
|
+
];
|
|
107
|
+
const entries = [
|
|
108
|
+
' entries:',
|
|
109
|
+
...task.entries.map((entry) =>
|
|
110
|
+
[
|
|
111
|
+
` – `,
|
|
112
|
+
chalk.green(`${path.join(ctx.pkg.name, entry.path)}: `),
|
|
113
|
+
`${chalk.cyan(entry.entry)} ${chalk.gray('→')} ${chalk.cyan(task.output)}`,
|
|
114
|
+
].join('')
|
|
115
|
+
),
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
this._spinner = ora(`Building javascript files:\n`).start();
|
|
119
|
+
|
|
120
|
+
ctx.logger.log([` format: ${task.format}`, ...targetLines, ...entries].join('\n'));
|
|
121
|
+
},
|
|
122
|
+
async run(ctx, task) {
|
|
123
|
+
try {
|
|
124
|
+
const config = resolveViteConfig(ctx, task);
|
|
125
|
+
ctx.logger.debug('Vite config: \n', config);
|
|
126
|
+
await build(config);
|
|
127
|
+
await this.success(ctx, task);
|
|
128
|
+
} catch (err) {
|
|
129
|
+
this.fail(ctx, task, err);
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
async success() {
|
|
133
|
+
this._spinner.succeed('Built javascript files');
|
|
134
|
+
},
|
|
135
|
+
async fail(ctx, task, err) {
|
|
136
|
+
this._spinner.fail('Failed to build javascript files');
|
|
137
|
+
|
|
138
|
+
throw err;
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
module.exports = {
|
|
143
|
+
viteTask,
|
|
144
|
+
};
|
|
@@ -19,7 +19,7 @@ const cleanupDistDirectory = async (distDir) => {
|
|
|
19
19
|
}
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
module.exports = async ({ srcDir, distDir,
|
|
22
|
+
module.exports = async ({ srcDir, distDir, ignoreDiagnostics = false }) => {
|
|
23
23
|
const isTSProject = await tsUtils.isUsingTypeScript(srcDir);
|
|
24
24
|
|
|
25
25
|
if (!isTSProject) {
|
|
@@ -28,5 +28,5 @@ module.exports = async ({ srcDir, distDir, watch = false }) => {
|
|
|
28
28
|
|
|
29
29
|
await cleanupDistDirectory(distDir);
|
|
30
30
|
|
|
31
|
-
return tsUtils.compile(srcDir, {
|
|
31
|
+
return tsUtils.compile(srcDir, { configOptions: { ignoreDiagnostics } });
|
|
32
32
|
};
|
package/lib/commands/index.js
CHANGED
|
@@ -20,6 +20,7 @@ const strapiCommands = {
|
|
|
20
20
|
install: require('./actions/install/command'),
|
|
21
21
|
'middlewares/list': require('./actions/middlewares/list/command'),
|
|
22
22
|
new: require('./actions/new/command'),
|
|
23
|
+
'plugin/build': require('./actions/plugin/build-command/command'),
|
|
23
24
|
'policies/list': require('./actions/policies/list/command'),
|
|
24
25
|
report: require('./actions/report/command'),
|
|
25
26
|
'routes/list': require('./actions/routes/list/command'),
|
|
@@ -8,6 +8,9 @@ const { yellow, red, green } = require('chalk');
|
|
|
8
8
|
const { isString, isArray } = require('lodash/fp');
|
|
9
9
|
const resolveCwd = require('resolve-cwd');
|
|
10
10
|
const { has } = require('lodash/fp');
|
|
11
|
+
const { prompt } = require('inquirer');
|
|
12
|
+
const boxen = require('boxen');
|
|
13
|
+
const chalk = require('chalk');
|
|
11
14
|
|
|
12
15
|
const bytesPerKb = 1024;
|
|
13
16
|
const sizes = ['B ', 'KB', 'MB', 'GB', 'TB', 'PB'];
|
|
@@ -121,7 +124,10 @@ const assertCwdContainsStrapiProject = (name) => {
|
|
|
121
124
|
|
|
122
125
|
try {
|
|
123
126
|
const pkgJSON = require(`${process.cwd()}/package.json`);
|
|
124
|
-
if (
|
|
127
|
+
if (
|
|
128
|
+
!has('dependencies.@strapi/strapi', pkgJSON) &&
|
|
129
|
+
!has('devDependencies.@strapi/strapi', pkgJSON)
|
|
130
|
+
) {
|
|
125
131
|
logErrorAndExit(name);
|
|
126
132
|
}
|
|
127
133
|
} catch (err) {
|
|
@@ -156,6 +162,51 @@ const getLocalScript =
|
|
|
156
162
|
});
|
|
157
163
|
};
|
|
158
164
|
|
|
165
|
+
/**
|
|
166
|
+
* @description Notify users this is an experimental command and get them to approve first
|
|
167
|
+
* this can be opted out by passing `yes` as a property of the args object.
|
|
168
|
+
*
|
|
169
|
+
* @type {(args?: { force?: boolean }) => Promise<void>}
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```ts
|
|
173
|
+
* const { notifyExperimentalCommand } = require('../utils/helpers');
|
|
174
|
+
*
|
|
175
|
+
* const myCommand = async ({ force }) => {
|
|
176
|
+
* await notifyExperimentalCommand({ force });
|
|
177
|
+
* }
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
180
|
+
const notifyExperimentalCommand = async ({ force } = {}) => {
|
|
181
|
+
console.log(
|
|
182
|
+
boxen(
|
|
183
|
+
`The ${chalk.bold(
|
|
184
|
+
chalk.underline('plugin:build')
|
|
185
|
+
)} command is considered experimental, use at your own risk.`,
|
|
186
|
+
{
|
|
187
|
+
title: 'Warning',
|
|
188
|
+
padding: 1,
|
|
189
|
+
margin: 1,
|
|
190
|
+
align: 'center',
|
|
191
|
+
borderColor: 'yellow',
|
|
192
|
+
borderStyle: 'bold',
|
|
193
|
+
}
|
|
194
|
+
)
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
if (!force) {
|
|
198
|
+
const { confirmed } = await prompt({
|
|
199
|
+
type: 'confirm',
|
|
200
|
+
name: 'confirmed',
|
|
201
|
+
message: 'Do you want to continue?',
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
if (!confirmed) {
|
|
205
|
+
process.exit(0);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
159
210
|
module.exports = {
|
|
160
211
|
exitWith,
|
|
161
212
|
assertUrlHasProtocol,
|
|
@@ -163,4 +214,5 @@ module.exports = {
|
|
|
163
214
|
readableBytes,
|
|
164
215
|
getLocalScript,
|
|
165
216
|
assertCwdContainsStrapiProject,
|
|
217
|
+
notifyExperimentalCommand,
|
|
166
218
|
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {{ silent?: boolean; debug?: boolean; timestamp?: boolean; }} LoggerOptions
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {object} Logger
|
|
11
|
+
* @property {number} warnings
|
|
12
|
+
* @property {number} errors
|
|
13
|
+
* @property {(...args: any[]) => void} debug
|
|
14
|
+
* @property {(...args: any[]) => void} info
|
|
15
|
+
* @property {(...args: any[]) => void} warn
|
|
16
|
+
* @property {(...args: any[]) => void} error
|
|
17
|
+
* @property {(...args: any[]) => void} log
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @type {(options: LoggerOptions) => Logger}
|
|
22
|
+
*/
|
|
23
|
+
const createLogger = (options = {}) => {
|
|
24
|
+
const { silent = false, debug = false, timestamp = true } = options;
|
|
25
|
+
|
|
26
|
+
const state = { errors: 0, warning: 0 };
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
get warnings() {
|
|
30
|
+
return state.warning;
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
get errors() {
|
|
34
|
+
return state.errors;
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
debug(...args) {
|
|
38
|
+
if (silent || !debug) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
console.log(
|
|
43
|
+
chalk.cyan(`[DEBUG]${timestamp ? `\t[${new Date().toISOString()}]` : ''}`),
|
|
44
|
+
...args
|
|
45
|
+
);
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
info(...args) {
|
|
49
|
+
if (silent) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.info(
|
|
54
|
+
chalk.blue(`[INFO]${timestamp ? `\t[${new Date().toISOString()}]` : ''}`),
|
|
55
|
+
...args
|
|
56
|
+
);
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
log(...args) {
|
|
60
|
+
if (silent) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
console.info(chalk.blue(`${timestamp ? `\t[${new Date().toISOString()}]` : ''}`), ...args);
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
warn(...args) {
|
|
68
|
+
state.warning += 1;
|
|
69
|
+
|
|
70
|
+
if (silent) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.warn(
|
|
75
|
+
chalk.yellow(`[WARN]${timestamp ? `\t[${new Date().toISOString()}]` : ''}`),
|
|
76
|
+
...args
|
|
77
|
+
);
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
error(...args) {
|
|
81
|
+
state.errors += 1;
|
|
82
|
+
|
|
83
|
+
if (silent) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
console.error(
|
|
88
|
+
chalk.red(`[ERROR]${timestamp ? `\t[${new Date().toISOString()}]` : ''}`),
|
|
89
|
+
...args
|
|
90
|
+
);
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
module.exports = {
|
|
96
|
+
createLogger,
|
|
97
|
+
};
|