@file-viewer/cli 3.0.0
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/LICENSE +160 -0
- package/README.en.md +109 -0
- package/README.md +109 -0
- package/catalog/catalog.json +1919 -0
- package/dist/carrier-command.d.ts +17 -0
- package/dist/carrier-command.js +47 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +1992 -0
- package/dist/copy-assets.d.ts +2 -0
- package/dist/copy-assets.js +4 -0
- package/dist/index.d.ts +131 -0
- package/dist/index.js +1665 -0
- package/dist/offline.d.ts +38 -0
- package/dist/offline.js +264 -0
- package/dist/project-adapters.d.ts +20 -0
- package/dist/project-adapters.js +440 -0
- package/dist/types.d.ts +210 -0
- package/dist/types.js +1 -0
- package/dist/url-security.d.ts +2 -0
- package/dist/url-security.js +58 -0
- package/package.json +78 -0
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { isAbsolute, relative, resolve, sep } from 'node:path';
|
|
4
|
+
const conventionalViteConfigs = [
|
|
5
|
+
'vite.config.ts',
|
|
6
|
+
'vite.config.js',
|
|
7
|
+
'vite.config.mts',
|
|
8
|
+
'vite.config.mjs',
|
|
9
|
+
'vite.config.cts',
|
|
10
|
+
'vite.config.cjs',
|
|
11
|
+
];
|
|
12
|
+
const conventionalWebpackConfigs = [
|
|
13
|
+
'webpack.config.ts',
|
|
14
|
+
'webpack.config.js',
|
|
15
|
+
'webpack.config.mjs',
|
|
16
|
+
'webpack.config.cjs',
|
|
17
|
+
];
|
|
18
|
+
const conventionalNuxtConfigs = [
|
|
19
|
+
'nuxt.config.ts',
|
|
20
|
+
'nuxt.config.js',
|
|
21
|
+
'nuxt.config.mjs',
|
|
22
|
+
];
|
|
23
|
+
const conventionalVueCliConfigs = [
|
|
24
|
+
'vue.config.ts',
|
|
25
|
+
'vue.config.js',
|
|
26
|
+
'vue.config.cjs',
|
|
27
|
+
];
|
|
28
|
+
const normalizeProjectPath = (projectRoot, value, label) => {
|
|
29
|
+
const trimmed = value.trim().replace(/\\/g, '/').replace(/^\.\//, '').replace(/\/$/, '');
|
|
30
|
+
if (!trimmed || trimmed === '.')
|
|
31
|
+
throw new Error(`${label} must not be the project root.`);
|
|
32
|
+
const absolute = resolve(projectRoot, trimmed);
|
|
33
|
+
const projectRelative = relative(resolve(projectRoot), absolute);
|
|
34
|
+
if (!projectRelative || projectRelative === '..' || projectRelative.startsWith(`..${sep}`) || isAbsolute(projectRelative)) {
|
|
35
|
+
throw new Error(`${label} must be a contained project-relative path.`);
|
|
36
|
+
}
|
|
37
|
+
return projectRelative.split(sep).join('/');
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Tokenizes only the shell syntax needed to inspect package scripts. It never
|
|
41
|
+
* executes or expands variables. Shell substitutions deliberately make the
|
|
42
|
+
* result unsafe so the adapter can fail closed.
|
|
43
|
+
*/
|
|
44
|
+
const tokenizePackageScript = (command) => {
|
|
45
|
+
const tokens = [];
|
|
46
|
+
let current = '';
|
|
47
|
+
let quote = null;
|
|
48
|
+
let escaped = false;
|
|
49
|
+
let unsafe = false;
|
|
50
|
+
for (let index = 0; index < command.length; index += 1) {
|
|
51
|
+
const character = command[index];
|
|
52
|
+
if (escaped) {
|
|
53
|
+
current += character;
|
|
54
|
+
escaped = false;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (character === '\\' && quote !== "'") {
|
|
58
|
+
escaped = true;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (quote) {
|
|
62
|
+
if (character === quote)
|
|
63
|
+
quote = null;
|
|
64
|
+
else {
|
|
65
|
+
if (character === '$' || character === '`')
|
|
66
|
+
unsafe = true;
|
|
67
|
+
current += character;
|
|
68
|
+
}
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (character === '"' || character === "'") {
|
|
72
|
+
quote = character;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (character === '$' || character === '`' || character === '\n' || character === '\r')
|
|
76
|
+
unsafe = true;
|
|
77
|
+
if (/\s/.test(character)) {
|
|
78
|
+
if (current)
|
|
79
|
+
tokens.push(current);
|
|
80
|
+
current = '';
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if ([';', '|', '&', '>', '<'].includes(character)) {
|
|
84
|
+
if (current)
|
|
85
|
+
tokens.push(current);
|
|
86
|
+
current = '';
|
|
87
|
+
tokens.push(character);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
current += character;
|
|
91
|
+
}
|
|
92
|
+
if (escaped || quote)
|
|
93
|
+
unsafe = true;
|
|
94
|
+
if (current)
|
|
95
|
+
tokens.push(current);
|
|
96
|
+
return { tokens, unsafe };
|
|
97
|
+
};
|
|
98
|
+
const executableName = (token) => token.replace(/\\/g, '/').split('/').at(-1)?.replace(/\.(?:cmd|exe)$/i, '') ?? token;
|
|
99
|
+
const scriptUses = (command, executables) => {
|
|
100
|
+
const { tokens } = tokenizePackageScript(command);
|
|
101
|
+
return tokens.some(token => executables.includes(executableName(token)));
|
|
102
|
+
};
|
|
103
|
+
const findConfigArguments = (scripts, executable) => {
|
|
104
|
+
const values = [];
|
|
105
|
+
let unsafe = false;
|
|
106
|
+
let optionSeenWithoutValue = false;
|
|
107
|
+
for (const script of scripts) {
|
|
108
|
+
const parsed = tokenizePackageScript(script.command);
|
|
109
|
+
unsafe ||= parsed.unsafe;
|
|
110
|
+
for (let index = 0; index < parsed.tokens.length; index += 1) {
|
|
111
|
+
if (executableName(parsed.tokens[index]) !== executable)
|
|
112
|
+
continue;
|
|
113
|
+
for (let cursor = index + 1; cursor < parsed.tokens.length; cursor += 1) {
|
|
114
|
+
const token = parsed.tokens[cursor];
|
|
115
|
+
if ([';', '|', '&'].includes(token))
|
|
116
|
+
break;
|
|
117
|
+
if (token === '--config' || token === '-c') {
|
|
118
|
+
const value = parsed.tokens[cursor + 1];
|
|
119
|
+
if (!value || value.startsWith('-') || [';', '|', '&'].includes(value))
|
|
120
|
+
optionSeenWithoutValue = true;
|
|
121
|
+
else
|
|
122
|
+
values.push(value);
|
|
123
|
+
}
|
|
124
|
+
else if (token.startsWith('--config=')) {
|
|
125
|
+
const value = token.slice('--config='.length);
|
|
126
|
+
if (value)
|
|
127
|
+
values.push(value);
|
|
128
|
+
else
|
|
129
|
+
optionSeenWithoutValue = true;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return { values: [...new Set(values)], unsafe, optionSeenWithoutValue };
|
|
135
|
+
};
|
|
136
|
+
const stripJavaScriptComments = (source) => {
|
|
137
|
+
let output = '';
|
|
138
|
+
let quote = null;
|
|
139
|
+
let escaped = false;
|
|
140
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
141
|
+
const character = source[index];
|
|
142
|
+
const next = source[index + 1];
|
|
143
|
+
if (quote) {
|
|
144
|
+
output += character;
|
|
145
|
+
if (escaped)
|
|
146
|
+
escaped = false;
|
|
147
|
+
else if (character === '\\')
|
|
148
|
+
escaped = true;
|
|
149
|
+
else if (character === quote)
|
|
150
|
+
quote = null;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (character === '"' || character === "'" || character === '`') {
|
|
154
|
+
quote = character;
|
|
155
|
+
output += character;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (character === '/' && next === '/') {
|
|
159
|
+
while (index < source.length && source[index] !== '\n')
|
|
160
|
+
index += 1;
|
|
161
|
+
output += '\n';
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (character === '/' && next === '*') {
|
|
165
|
+
index += 2;
|
|
166
|
+
while (index < source.length && !(source[index] === '*' && source[index + 1] === '/'))
|
|
167
|
+
index += 1;
|
|
168
|
+
index += 1;
|
|
169
|
+
output += ' ';
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
output += character;
|
|
173
|
+
}
|
|
174
|
+
return output;
|
|
175
|
+
};
|
|
176
|
+
const readSimpleStringProperty = (source, property) => {
|
|
177
|
+
const stripped = stripJavaScriptComments(source);
|
|
178
|
+
const occurrence = new RegExp(`\\b${property.replace(/[.*+?^${}()|[\\]\\]/g, '\\$&')}\\s*:`, 'g');
|
|
179
|
+
const matches = [...stripped.matchAll(occurrence)];
|
|
180
|
+
if (!matches.length)
|
|
181
|
+
return { kind: 'absent' };
|
|
182
|
+
if (matches.length !== 1)
|
|
183
|
+
return { kind: 'dynamic' };
|
|
184
|
+
const tail = stripped.slice((matches[0].index ?? 0) + matches[0][0].length).trimStart();
|
|
185
|
+
if (tail.startsWith('false'))
|
|
186
|
+
return { kind: 'disabled' };
|
|
187
|
+
const quote = tail[0];
|
|
188
|
+
if (!['"', "'", '`'].includes(quote))
|
|
189
|
+
return { kind: 'dynamic' };
|
|
190
|
+
let value = '';
|
|
191
|
+
let escaped = false;
|
|
192
|
+
for (let index = 1; index < tail.length; index += 1) {
|
|
193
|
+
const character = tail[index];
|
|
194
|
+
if (escaped) {
|
|
195
|
+
if (!['\\', '/', '"', "'", '`'].includes(character))
|
|
196
|
+
return { kind: 'dynamic' };
|
|
197
|
+
value += character;
|
|
198
|
+
escaped = false;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (character === '\\') {
|
|
202
|
+
escaped = true;
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (character === quote) {
|
|
206
|
+
if (quote === '`' && value.includes('${'))
|
|
207
|
+
return { kind: 'dynamic' };
|
|
208
|
+
return { kind: 'literal', value };
|
|
209
|
+
}
|
|
210
|
+
value += character;
|
|
211
|
+
}
|
|
212
|
+
return { kind: 'dynamic' };
|
|
213
|
+
};
|
|
214
|
+
const readNuxtDirectoryProperty = (source, property) => {
|
|
215
|
+
const stripped = stripJavaScriptComments(source);
|
|
216
|
+
const matches = [...stripped.matchAll(/\bdir\s*:/g)];
|
|
217
|
+
if (!matches.length)
|
|
218
|
+
return { kind: 'absent' };
|
|
219
|
+
if (matches.length !== 1)
|
|
220
|
+
return { kind: 'dynamic' };
|
|
221
|
+
const start = (matches[0].index ?? 0) + matches[0][0].length;
|
|
222
|
+
const objectStart = stripped.slice(start).search(/\S/) + start;
|
|
223
|
+
if (objectStart < start || stripped[objectStart] !== '{')
|
|
224
|
+
return { kind: 'dynamic' };
|
|
225
|
+
let depth = 0;
|
|
226
|
+
let quote = null;
|
|
227
|
+
let escaped = false;
|
|
228
|
+
for (let index = objectStart; index < stripped.length; index += 1) {
|
|
229
|
+
const character = stripped[index];
|
|
230
|
+
if (quote) {
|
|
231
|
+
if (escaped)
|
|
232
|
+
escaped = false;
|
|
233
|
+
else if (character === '\\')
|
|
234
|
+
escaped = true;
|
|
235
|
+
else if (character === quote)
|
|
236
|
+
quote = null;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if (character === '"' || character === "'" || character === '`') {
|
|
240
|
+
quote = character;
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (character === '{')
|
|
244
|
+
depth += 1;
|
|
245
|
+
if (character === '}') {
|
|
246
|
+
depth -= 1;
|
|
247
|
+
if (depth === 0)
|
|
248
|
+
return readSimpleStringProperty(stripped.slice(objectStart, index + 1), property);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return { kind: 'dynamic' };
|
|
252
|
+
};
|
|
253
|
+
const dependencyMajor = (value) => Number(value?.match(/(?:^|[^0-9])(\d+)(?:\.|$)/)?.[1] ?? NaN);
|
|
254
|
+
const adapterResult = (buildSystem, values) => ({
|
|
255
|
+
schemaVersion: 1,
|
|
256
|
+
buildSystem,
|
|
257
|
+
safeAutomaticConfiguration: Boolean(values.assetTarget) && values.manualSteps.length === 0,
|
|
258
|
+
failClosed: !values.assetTarget || values.manualSteps.length > 0,
|
|
259
|
+
...values,
|
|
260
|
+
});
|
|
261
|
+
const conventionalFiles = (projectRoot, names) => names.filter(name => existsSync(resolve(projectRoot, name)));
|
|
262
|
+
export async function inspectFileViewerProjectAdapter(projectRoot) {
|
|
263
|
+
const root = resolve(projectRoot);
|
|
264
|
+
const manifest = JSON.parse(await readFile(resolve(root, 'package.json'), 'utf8'));
|
|
265
|
+
const dependencies = { ...manifest.dependencies, ...manifest.devDependencies };
|
|
266
|
+
const scripts = Object.entries(manifest.scripts ?? {})
|
|
267
|
+
.filter((entry) => typeof entry[1] === 'string')
|
|
268
|
+
.map(([name, command]) => ({ name, command }));
|
|
269
|
+
const relevantScripts = scripts.filter(item => scriptUses(item.command, ['vite', 'vue-cli-service', 'webpack', 'webpack-cli', 'next', 'nuxt', 'nuxt2', 'nuxi']));
|
|
270
|
+
const usesVite = Boolean(dependencies.vite) || relevantScripts.some(item => scriptUses(item.command, ['vite']));
|
|
271
|
+
const usesVueCli = Boolean(dependencies['@vue/cli-service']) || relevantScripts.some(item => scriptUses(item.command, ['vue-cli-service']));
|
|
272
|
+
const usesNext = Boolean(dependencies.next) || relevantScripts.some(item => scriptUses(item.command, ['next']));
|
|
273
|
+
const usesNuxt = Boolean(dependencies.nuxt) || relevantScripts.some(item => scriptUses(item.command, ['nuxt', 'nuxt2', 'nuxi']));
|
|
274
|
+
const usesWebpack = Boolean(dependencies.webpack || dependencies['webpack-cli']) || relevantScripts.some(item => scriptUses(item.command, ['webpack', 'webpack-cli']));
|
|
275
|
+
const detected = [usesVite && 'vite', usesVueCli && 'vue-cli', usesNext && 'next', usesNuxt && 'nuxt', usesWebpack && 'webpack'].filter(Boolean);
|
|
276
|
+
const primary = ['build', 'dev', 'start'].map(name => scripts.find(script => script.name === name)).filter(Boolean);
|
|
277
|
+
const primarySystems = [
|
|
278
|
+
primary.some(item => scriptUses(item.command, ['vite'])) && 'vite',
|
|
279
|
+
primary.some(item => scriptUses(item.command, ['vue-cli-service'])) && 'vue-cli',
|
|
280
|
+
primary.some(item => scriptUses(item.command, ['next'])) && 'next',
|
|
281
|
+
primary.some(item => scriptUses(item.command, ['nuxt', 'nuxt2', 'nuxi'])) && 'nuxt',
|
|
282
|
+
primary.some(item => scriptUses(item.command, ['webpack', 'webpack-cli'])) && 'webpack',
|
|
283
|
+
].filter(Boolean);
|
|
284
|
+
const selectedSystems = [...new Set(primarySystems.length ? primarySystems : detected)];
|
|
285
|
+
if (selectedSystems.length > 1) {
|
|
286
|
+
return adapterResult('unknown', {
|
|
287
|
+
configPaths: [],
|
|
288
|
+
relevantScripts,
|
|
289
|
+
warnings: [],
|
|
290
|
+
manualSteps: [`Multiple build systems were detected (${selectedSystems.join(', ')}). Choose the application package or configure its static directory, then pass --asset-target explicitly.`],
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
const buildSystem = selectedSystems[0] ?? 'unknown';
|
|
294
|
+
if (buildSystem === 'vite') {
|
|
295
|
+
const viteScripts = relevantScripts.filter(item => scriptUses(item.command, ['vite']));
|
|
296
|
+
const argumentsResult = findConfigArguments(viteScripts, 'vite');
|
|
297
|
+
const manualSteps = [];
|
|
298
|
+
const warnings = [];
|
|
299
|
+
let configPaths = [];
|
|
300
|
+
if (argumentsResult.unsafe || argumentsResult.optionSeenWithoutValue) {
|
|
301
|
+
manualSteps.push('The Vite --config argument uses shell expansion or has no static value. Resolve it to one contained config file before running add.');
|
|
302
|
+
}
|
|
303
|
+
else if (argumentsResult.values.length) {
|
|
304
|
+
try {
|
|
305
|
+
configPaths = argumentsResult.values.map(value => normalizeProjectPath(root, value, 'Vite config path'));
|
|
306
|
+
}
|
|
307
|
+
catch (error) {
|
|
308
|
+
manualSteps.push(error.message);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
else
|
|
312
|
+
configPaths = conventionalFiles(root, conventionalViteConfigs);
|
|
313
|
+
configPaths = [...new Set(configPaths)];
|
|
314
|
+
if (configPaths.length > 1)
|
|
315
|
+
manualSteps.push(`Multiple Vite configs were detected (${configPaths.join(', ')}). Select one build target and pass a matching --asset-target.`);
|
|
316
|
+
const selectedConfigPath = configPaths.length === 1 ? configPaths[0] : undefined;
|
|
317
|
+
let publicDirectory;
|
|
318
|
+
if (selectedConfigPath && !existsSync(resolve(root, selectedConfigPath))) {
|
|
319
|
+
manualSteps.push(`The Vite config ${selectedConfigPath} referenced by package scripts does not exist.`);
|
|
320
|
+
}
|
|
321
|
+
else if (selectedConfigPath) {
|
|
322
|
+
const publicDir = readSimpleStringProperty(await readFile(resolve(root, selectedConfigPath), 'utf8'), 'publicDir');
|
|
323
|
+
if (publicDir.kind === 'literal') {
|
|
324
|
+
try {
|
|
325
|
+
publicDirectory = normalizeProjectPath(root, publicDir.value, 'Vite publicDir');
|
|
326
|
+
}
|
|
327
|
+
catch (error) {
|
|
328
|
+
manualSteps.push(error.message);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
else if (publicDir.kind === 'absent')
|
|
332
|
+
publicDirectory = 'public';
|
|
333
|
+
else if (publicDir.kind === 'disabled')
|
|
334
|
+
manualSteps.push(`Vite publicDir is disabled in ${selectedConfigPath}. Configure an explicit copied static directory before running add.`);
|
|
335
|
+
else
|
|
336
|
+
manualSteps.push(`Vite publicDir in ${selectedConfigPath} is dynamic. Resolve its actual static directory and pass --asset-target explicitly.`);
|
|
337
|
+
}
|
|
338
|
+
else if (!manualSteps.length)
|
|
339
|
+
publicDirectory = 'public';
|
|
340
|
+
const assetTarget = publicDirectory ? `${publicDirectory}/file-viewer` : undefined;
|
|
341
|
+
return adapterResult('vite', { assetTarget, publicDirectory, selectedConfigPath, configPaths, relevantScripts, manualSteps, warnings });
|
|
342
|
+
}
|
|
343
|
+
if (buildSystem === 'vue-cli') {
|
|
344
|
+
const configPaths = conventionalFiles(root, conventionalVueCliConfigs);
|
|
345
|
+
const manualSteps = configPaths.length > 1 ? [`Multiple Vue CLI configs were detected (${configPaths.join(', ')}). Keep one application config before running add.`] : [];
|
|
346
|
+
return adapterResult('vue-cli', {
|
|
347
|
+
assetTarget: manualSteps.length ? undefined : 'public/file-viewer',
|
|
348
|
+
publicDirectory: manualSteps.length ? undefined : 'public',
|
|
349
|
+
selectedConfigPath: configPaths.length === 1 ? configPaths[0] : undefined,
|
|
350
|
+
configPaths,
|
|
351
|
+
relevantScripts,
|
|
352
|
+
manualSteps,
|
|
353
|
+
warnings: [],
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
if (buildSystem === 'next') {
|
|
357
|
+
const configPaths = conventionalFiles(root, ['next.config.ts', 'next.config.js', 'next.config.mjs']);
|
|
358
|
+
return adapterResult('next', {
|
|
359
|
+
assetTarget: 'public/file-viewer',
|
|
360
|
+
publicDirectory: 'public',
|
|
361
|
+
selectedConfigPath: configPaths.length === 1 ? configPaths[0] : undefined,
|
|
362
|
+
configPaths,
|
|
363
|
+
relevantScripts,
|
|
364
|
+
manualSteps: [],
|
|
365
|
+
warnings: ['Next.js serves files in public at the site root; File Viewer assets will be available under /file-viewer/.'],
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
if (buildSystem === 'nuxt') {
|
|
369
|
+
const configPaths = conventionalFiles(root, conventionalNuxtConfigs);
|
|
370
|
+
const manualSteps = configPaths.length > 1 ? [`Multiple Nuxt configs were detected (${configPaths.join(', ')}). Keep one application config before running add.`] : [];
|
|
371
|
+
const major = dependencyMajor(dependencies.nuxt);
|
|
372
|
+
const property = Number.isFinite(major) && major <= 2 ? 'static' : 'public';
|
|
373
|
+
let publicDirectory = property;
|
|
374
|
+
const selectedConfigPath = configPaths.length === 1 ? configPaths[0] : undefined;
|
|
375
|
+
if (selectedConfigPath) {
|
|
376
|
+
const configured = readNuxtDirectoryProperty(await readFile(resolve(root, selectedConfigPath), 'utf8'), property);
|
|
377
|
+
if (configured.kind === 'literal') {
|
|
378
|
+
try {
|
|
379
|
+
publicDirectory = normalizeProjectPath(root, configured.value, `Nuxt dir.${property}`);
|
|
380
|
+
}
|
|
381
|
+
catch (error) {
|
|
382
|
+
manualSteps.push(error.message);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
else if (configured.kind === 'dynamic' || configured.kind === 'disabled') {
|
|
386
|
+
manualSteps.push(`Nuxt dir.${property} in ${selectedConfigPath} is not a static directory. Resolve it and pass --asset-target explicitly.`);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return adapterResult('nuxt', {
|
|
390
|
+
assetTarget: manualSteps.length ? undefined : `${publicDirectory}/file-viewer`,
|
|
391
|
+
publicDirectory: manualSteps.length ? undefined : publicDirectory,
|
|
392
|
+
selectedConfigPath,
|
|
393
|
+
configPaths,
|
|
394
|
+
relevantScripts,
|
|
395
|
+
manualSteps,
|
|
396
|
+
warnings: [],
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
if (buildSystem === 'webpack') {
|
|
400
|
+
const scriptConfigs = findConfigArguments(relevantScripts.filter(item => scriptUses(item.command, ['webpack', 'webpack-cli'])), 'webpack');
|
|
401
|
+
let configPaths = [];
|
|
402
|
+
try {
|
|
403
|
+
configPaths = scriptConfigs.values.length
|
|
404
|
+
? scriptConfigs.values.map(value => normalizeProjectPath(root, value, 'Webpack config path'))
|
|
405
|
+
: conventionalFiles(root, conventionalWebpackConfigs);
|
|
406
|
+
}
|
|
407
|
+
catch {
|
|
408
|
+
// The manual step below deliberately handles unsafe/uncontained configs.
|
|
409
|
+
}
|
|
410
|
+
return adapterResult('webpack', {
|
|
411
|
+
selectedConfigPath: configPaths.length === 1 ? configPaths[0] : undefined,
|
|
412
|
+
configPaths: [...new Set(configPaths)],
|
|
413
|
+
relevantScripts,
|
|
414
|
+
manualSteps: ['Generic Webpack has no standard public source directory. Configure CopyWebpackPlugin (or an equivalent static copy) for a dedicated directory, then pass that directory as --asset-target.'],
|
|
415
|
+
warnings: [],
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
return adapterResult('unknown', {
|
|
419
|
+
configPaths: [],
|
|
420
|
+
relevantScripts,
|
|
421
|
+
manualSteps: ['No supported build adapter was detected. Configure a project-relative static directory that is copied unchanged to the public build, then pass it as --asset-target.'],
|
|
422
|
+
warnings: [],
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
export function assertFileViewerProjectAdapterCanWrite(inspection) {
|
|
426
|
+
if (!inspection.safeAutomaticConfiguration || inspection.failClosed) {
|
|
427
|
+
throw new Error(`File Viewer cannot safely complete this project integration automatically:\n- ${inspection.manualSteps.join('\n- ')}`);
|
|
428
|
+
}
|
|
429
|
+
return inspection;
|
|
430
|
+
}
|
|
431
|
+
export function readDeclaredPackageManagerVersion(projectRoot, manager) {
|
|
432
|
+
try {
|
|
433
|
+
const manifest = JSON.parse(readFileSync(resolve(projectRoot, 'package.json'), 'utf8'));
|
|
434
|
+
const match = manifest.packageManager?.match(new RegExp(`^${manager.replace(/[.*+?^${}()|[\\]\\]/g, '\\$&')}@([^\\s]+)$`, 'i'));
|
|
435
|
+
return match?.[1];
|
|
436
|
+
}
|
|
437
|
+
catch {
|
|
438
|
+
return undefined;
|
|
439
|
+
}
|
|
440
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
export type FileViewerFramework = 'web' | 'vue3' | 'vue2.7' | 'vue2.6' | 'react' | 'react-legacy' | 'svelte' | 'jquery';
|
|
2
|
+
export type FileViewerProfile = 'standard' | 'lite' | 'office' | 'engineering' | 'all' | 'full' | 'custom';
|
|
3
|
+
export type PackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun';
|
|
4
|
+
export type FileViewerCliLocale = 'en' | 'zh-CN' | 'ja-JP' | 'de-DE';
|
|
5
|
+
export type FileViewerInstallSource = {
|
|
6
|
+
kind: 'registry';
|
|
7
|
+
registry?: string;
|
|
8
|
+
cacheDir?: string;
|
|
9
|
+
concurrency?: number;
|
|
10
|
+
} | {
|
|
11
|
+
kind: 'offline-directory';
|
|
12
|
+
directory: string;
|
|
13
|
+
cacheDir?: string;
|
|
14
|
+
concurrency?: number;
|
|
15
|
+
};
|
|
16
|
+
export interface FileViewerCapabilityAssetDeclaration {
|
|
17
|
+
rendererIds: string[];
|
|
18
|
+
packageName?: string;
|
|
19
|
+
packageVersion?: string;
|
|
20
|
+
installerPackageName?: string;
|
|
21
|
+
installerPackageVersion?: string;
|
|
22
|
+
bin?: string;
|
|
23
|
+
apiExport?: string;
|
|
24
|
+
target?: string;
|
|
25
|
+
copyGroups?: string[];
|
|
26
|
+
copyMode?: 'profile-pack' | 'capability-pack' | 'renderer-groups';
|
|
27
|
+
receiptFilename?: string;
|
|
28
|
+
notice?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface FileViewerCapabilityCatalogEntry {
|
|
31
|
+
id: string;
|
|
32
|
+
packageName: string;
|
|
33
|
+
version: string;
|
|
34
|
+
activation?: {
|
|
35
|
+
kind: 'renderer-export' | 'side-effect-import' | 'loader-registration';
|
|
36
|
+
import: string;
|
|
37
|
+
export?: string;
|
|
38
|
+
};
|
|
39
|
+
rendererIds: string[];
|
|
40
|
+
formats: string[];
|
|
41
|
+
assets: FileViewerCapabilityAssetDeclaration;
|
|
42
|
+
license: {
|
|
43
|
+
spdx: string;
|
|
44
|
+
policy: 'permissive' | 'separately-licensed' | 'review-required';
|
|
45
|
+
notices?: Array<{
|
|
46
|
+
packageName: string;
|
|
47
|
+
spdx: string;
|
|
48
|
+
notice?: string;
|
|
49
|
+
}>;
|
|
50
|
+
};
|
|
51
|
+
weight: 'light' | 'standard' | 'heavy';
|
|
52
|
+
profiles: string[];
|
|
53
|
+
}
|
|
54
|
+
export interface FileViewerCapabilityListEntry {
|
|
55
|
+
id: string;
|
|
56
|
+
packageSpec: string;
|
|
57
|
+
formats: string[];
|
|
58
|
+
rendererIds: string[];
|
|
59
|
+
weight: FileViewerCapabilityCatalogEntry['weight'];
|
|
60
|
+
license: FileViewerCapabilityCatalogEntry['license'];
|
|
61
|
+
profiles: string[];
|
|
62
|
+
availability: string;
|
|
63
|
+
assetPackageSpec: string | null;
|
|
64
|
+
}
|
|
65
|
+
export interface FileViewerCapabilityList {
|
|
66
|
+
schemaVersion: 1;
|
|
67
|
+
coreVersion: string;
|
|
68
|
+
capabilities: FileViewerCapabilityListEntry[];
|
|
69
|
+
}
|
|
70
|
+
export interface FileViewerProfileCatalogEntry {
|
|
71
|
+
id: string;
|
|
72
|
+
packageName: string;
|
|
73
|
+
version: string;
|
|
74
|
+
capabilityPackages: string[];
|
|
75
|
+
assetPackageName?: string;
|
|
76
|
+
profileManifestSha256?: string;
|
|
77
|
+
estimates?: {
|
|
78
|
+
packedClosureBytes: number;
|
|
79
|
+
unpackedClosureBytes: number;
|
|
80
|
+
staticAssetBytes: number;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
export interface FileViewerCliCatalog {
|
|
84
|
+
schemaVersion: 1;
|
|
85
|
+
core: {
|
|
86
|
+
packageName: '@file-viewer/core';
|
|
87
|
+
version: string;
|
|
88
|
+
};
|
|
89
|
+
frameworks: Record<FileViewerFramework, {
|
|
90
|
+
packageName: string;
|
|
91
|
+
version: string;
|
|
92
|
+
}>;
|
|
93
|
+
frameworkOverrides?: Partial<Record<FileViewerProfile, Partial<Record<FileViewerFramework, {
|
|
94
|
+
packageName: string;
|
|
95
|
+
version: string;
|
|
96
|
+
}>>>>;
|
|
97
|
+
frameworkTemplates?: Record<FileViewerFramework, {
|
|
98
|
+
defaultVersion: string;
|
|
99
|
+
runtimeDependencies: Record<string, string>;
|
|
100
|
+
viteVersion: string;
|
|
101
|
+
validatedVersions: Record<string, {
|
|
102
|
+
runtimeDependencies: Record<string, string>;
|
|
103
|
+
viteVersion: string;
|
|
104
|
+
vitePluginSvelteVersion?: string;
|
|
105
|
+
templateVariant?: string;
|
|
106
|
+
}>;
|
|
107
|
+
}>;
|
|
108
|
+
profiles: FileViewerProfileCatalogEntry[];
|
|
109
|
+
capabilities: FileViewerCapabilityCatalogEntry[];
|
|
110
|
+
assetTool: {
|
|
111
|
+
packageName: string;
|
|
112
|
+
version: string;
|
|
113
|
+
};
|
|
114
|
+
legacyFull?: {
|
|
115
|
+
release: string;
|
|
116
|
+
policy: 'legacy-compatible-frozen';
|
|
117
|
+
baselineSha256: string;
|
|
118
|
+
excludedFutureCapabilities: string[];
|
|
119
|
+
referenceColdInstall: {
|
|
120
|
+
measuredAt: string;
|
|
121
|
+
packageCountRange: [number, number];
|
|
122
|
+
packedBytesRange: [number, number];
|
|
123
|
+
unpackedBytesRange: [number, number];
|
|
124
|
+
registry: string;
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
export interface FileViewerCommandStep {
|
|
129
|
+
id: string;
|
|
130
|
+
kind: 'install' | 'assets';
|
|
131
|
+
command: string;
|
|
132
|
+
args: string[];
|
|
133
|
+
cwd: string;
|
|
134
|
+
env?: Record<string, string>;
|
|
135
|
+
expectedExecutableVersion?: string;
|
|
136
|
+
executableOwner?: {
|
|
137
|
+
packageName: string;
|
|
138
|
+
packageVersion: string;
|
|
139
|
+
bin: string;
|
|
140
|
+
};
|
|
141
|
+
assetOwner?: {
|
|
142
|
+
packageName: string;
|
|
143
|
+
packageVersion: string;
|
|
144
|
+
target: string;
|
|
145
|
+
copyGroups: string[];
|
|
146
|
+
receiptFilename: string;
|
|
147
|
+
expectedProfileManifestSha256?: string;
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
export interface FileViewerProjectConfig {
|
|
151
|
+
schemaVersion: 1;
|
|
152
|
+
framework: FileViewerFramework;
|
|
153
|
+
profile: FileViewerProfile;
|
|
154
|
+
formats: string[];
|
|
155
|
+
capabilities: string[];
|
|
156
|
+
assetTarget: string;
|
|
157
|
+
generatedModule: string;
|
|
158
|
+
entry?: string;
|
|
159
|
+
packageManager?: PackageManager;
|
|
160
|
+
packageManagerVersion?: string;
|
|
161
|
+
managedPackages?: string[];
|
|
162
|
+
locale?: FileViewerCliLocale;
|
|
163
|
+
source?: FileViewerInstallSource;
|
|
164
|
+
frameworkVersion?: string;
|
|
165
|
+
assetBaseUrl?: string;
|
|
166
|
+
}
|
|
167
|
+
export interface FileViewerInstallPlan {
|
|
168
|
+
schemaVersion: 1;
|
|
169
|
+
framework: FileViewerFramework;
|
|
170
|
+
profile: FileViewerProfile;
|
|
171
|
+
packageManager: PackageManager;
|
|
172
|
+
packageManagerVersion?: string;
|
|
173
|
+
packages: string[];
|
|
174
|
+
packageSpecs: string[];
|
|
175
|
+
requiredPackages: Array<{
|
|
176
|
+
packageName: string;
|
|
177
|
+
version: string;
|
|
178
|
+
}>;
|
|
179
|
+
capabilityPackages: string[];
|
|
180
|
+
heavyCapabilities: string[];
|
|
181
|
+
licenseNotices: Array<{
|
|
182
|
+
packageName: string;
|
|
183
|
+
spdx: string;
|
|
184
|
+
policy: string;
|
|
185
|
+
}>;
|
|
186
|
+
assetPackages: string[];
|
|
187
|
+
assetRendererIds: string[];
|
|
188
|
+
missingAssetRendererIds: string[];
|
|
189
|
+
estimates: FileViewerProfileCatalogEntry['estimates'] | null;
|
|
190
|
+
legacyFullEstimate?: NonNullable<FileViewerCliCatalog['legacyFull']>['referenceColdInstall'];
|
|
191
|
+
steps: FileViewerCommandStep[];
|
|
192
|
+
command: string;
|
|
193
|
+
assetCommands: string[];
|
|
194
|
+
assetCommand: string;
|
|
195
|
+
assetTarget: string;
|
|
196
|
+
generatedModule: string;
|
|
197
|
+
integrationImport: string;
|
|
198
|
+
}
|
|
199
|
+
export interface FileViewerDoctorResult {
|
|
200
|
+
ok: boolean;
|
|
201
|
+
configPath: string;
|
|
202
|
+
errors: string[];
|
|
203
|
+
warnings: string[];
|
|
204
|
+
plan: FileViewerInstallPlan;
|
|
205
|
+
}
|
|
206
|
+
export interface FileViewerCommandExecutionResult {
|
|
207
|
+
executed: boolean;
|
|
208
|
+
step: FileViewerCommandStep;
|
|
209
|
+
status: number | null;
|
|
210
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|