@outputai/core 0.3.3-next.e8eff63.0 → 0.4.1-dev.622e67b.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/package.json +7 -2
- package/src/interface/workflow.d.ts +2 -2
- package/src/worker/bundler_options.js +33 -4
- package/src/worker/bundler_options.spec.js +62 -0
- package/src/worker/loader.js +62 -50
- package/src/worker/loader.spec.js +285 -82
- package/src/worker/loader_tools.js +232 -60
- package/src/worker/loader_tools.spec.js +496 -25
- package/src/worker/webpack_loaders/npm_workflow_export_resolve.js +474 -0
- package/src/worker/webpack_loaders/npm_workflow_export_resolve.spec.js +374 -0
- package/src/worker/webpack_loaders/tools.js +9 -0
- package/src/worker/webpack_loaders/tools.spec.js +7 -0
- package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.js +80 -11
- package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.spec.js +67 -0
- package/src/worker/webpack_loaders/workflow_rewriter/index.mjs +2 -1
- package/src/worker/webpack_loaders/workflow_rewriter/index.spec.js +73 -1
- package/src/worker/webpack_loaders/workflow_validator/index.mjs +66 -1
- package/src/worker/webpack_loaders/workflow_validator/index.spec.js +62 -0
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { dirname, resolve as resolvePath } from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import {
|
|
5
|
+
isExportAllDeclaration,
|
|
6
|
+
isExportNamedDeclaration,
|
|
7
|
+
isExportSpecifier,
|
|
8
|
+
isIdentifier,
|
|
9
|
+
isImportDefaultSpecifier,
|
|
10
|
+
isImportNamespaceSpecifier,
|
|
11
|
+
isImportSpecifier,
|
|
12
|
+
isStringLiteral
|
|
13
|
+
} from '@babel/types';
|
|
14
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
15
|
+
import { parse, isWorkflowPath, buildWorkflowNameMap } from './tools.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Resolves bare npm imports to workflow runtime names:
|
|
19
|
+
* - `require.resolve` from the importing file locates the package entry or subpath.
|
|
20
|
+
* - Follows `export { ... } from` and `export * from` until a module whose path ends in `workflow.js`.
|
|
21
|
+
* - Reads declared names via {@link buildWorkflowNameMap} on that terminal file.
|
|
22
|
+
*
|
|
23
|
+
* The rewriter applies this only when the importing resource is `workflow.js` (see collect_target_imports).
|
|
24
|
+
* The workflow validator also uses it for steps/evaluators so catalog imports are recognized in `fn`.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* True for npm-style bare specifiers we may resolve with `require.resolve`.
|
|
29
|
+
* Excludes relative paths, absolute paths, and built-in / URL protocols.
|
|
30
|
+
*
|
|
31
|
+
* @param {string} specifier - ESM import source or `require()` string.
|
|
32
|
+
* @returns {boolean}
|
|
33
|
+
*/
|
|
34
|
+
export const isBareNpmSpecifier = specifier => {
|
|
35
|
+
if ( typeof specifier !== 'string' || specifier.length === 0 ) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
if ( specifier.startsWith( '.' ) || specifier.startsWith( '/' ) ) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
if ( specifier.startsWith( 'node:' ) || specifier.startsWith( 'file:' ) ||
|
|
42
|
+
specifier.startsWith( 'data:' ) || specifier.startsWith( 'http:' ) ||
|
|
43
|
+
specifier.startsWith( 'https:' ) ) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
return true;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* True when the absolute path ends with a `workflow.js` segment (same rule as path-based workflow files).
|
|
51
|
+
*
|
|
52
|
+
* @param {string} absolutePath - Resolved absolute file path.
|
|
53
|
+
* @returns {boolean}
|
|
54
|
+
*/
|
|
55
|
+
const absolutePathIsWorkflowJsFile = absolutePath =>
|
|
56
|
+
isWorkflowPath( absolutePath.replace( /\\/g, '/' ) );
|
|
57
|
+
|
|
58
|
+
const unsupportedNamespaceWorkflowImportError = specifier => new Error(
|
|
59
|
+
`Namespace imports from workflow package "${specifier}" are not supported. ` +
|
|
60
|
+
`Use named imports instead, e.g. import { myWorkflow } from '${specifier}'.`
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Split a bare npm specifier into its package name and package export subpath.
|
|
65
|
+
*
|
|
66
|
+
* Handles both unscoped packages (`pkg/path`) and scoped packages (`@scope/pkg/path`).
|
|
67
|
+
*
|
|
68
|
+
* @param {string} specifier - Bare npm specifier from an import or require call.
|
|
69
|
+
* @returns {{ packageName: string, subpath: string }} Package name and exports-style subpath.
|
|
70
|
+
*/
|
|
71
|
+
const packagePartsFromSpecifier = specifier => {
|
|
72
|
+
const parts = specifier.split( '/' );
|
|
73
|
+
if ( specifier.startsWith( '@' ) ) {
|
|
74
|
+
return {
|
|
75
|
+
packageName: parts.slice( 0, 2 ).join( '/' ),
|
|
76
|
+
subpath: parts.length > 2 ? `./${parts.slice( 2 ).join( '/' )}` : '.'
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
packageName: parts[0],
|
|
81
|
+
subpath: parts.length > 1 ? `./${parts.slice( 1 ).join( '/' )}` : '.'
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Walk upward from a source directory to find the package.json for an installed dependency.
|
|
87
|
+
*
|
|
88
|
+
* Mirrors Node's nearest `node_modules` lookup so temporary tests and nested workspace layouts resolve consistently.
|
|
89
|
+
*
|
|
90
|
+
* @param {string} fromDir - Directory where package resolution starts.
|
|
91
|
+
* @param {string} packageName - Bare package name, including scope for scoped packages.
|
|
92
|
+
* @returns {string|null} Absolute package.json path, or null when the package is not installed.
|
|
93
|
+
*/
|
|
94
|
+
const findPackageJson = ( fromDir, packageName ) => {
|
|
95
|
+
const candidate = resolvePath( fromDir, 'node_modules', packageName, 'package.json' );
|
|
96
|
+
if ( existsSync( candidate ) ) {
|
|
97
|
+
return candidate;
|
|
98
|
+
}
|
|
99
|
+
const parent = dirname( fromDir );
|
|
100
|
+
return parent !== fromDir ? findPackageJson( parent, packageName ) : null;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Choose the best concrete file target from a package exports target.
|
|
105
|
+
*
|
|
106
|
+
* Prefers the workflow-specific webpack condition, then the same fallback conditions used by
|
|
107
|
+
* `webpackConfigHook`.
|
|
108
|
+
*
|
|
109
|
+
* @param {string|Array|object|null|undefined} target - Value from package.json `exports`.
|
|
110
|
+
* @returns {string|null} Relative file target, or null when no supported condition resolves.
|
|
111
|
+
*/
|
|
112
|
+
const resolveConditionalExportTarget = target => {
|
|
113
|
+
if ( typeof target === 'string' ) {
|
|
114
|
+
return target;
|
|
115
|
+
}
|
|
116
|
+
if ( Array.isArray( target ) ) {
|
|
117
|
+
for ( const item of target ) {
|
|
118
|
+
const resolved = resolveConditionalExportTarget( item );
|
|
119
|
+
if ( resolved ) {
|
|
120
|
+
return resolved;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
if ( !target || typeof target !== 'object' ) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
for ( const condition of [ 'output-workflow-bundle', 'import', 'module', 'webpack', 'default' ] ) {
|
|
130
|
+
if ( condition in target ) {
|
|
131
|
+
const resolved = resolveConditionalExportTarget( target[condition] );
|
|
132
|
+
if ( resolved ) {
|
|
133
|
+
return resolved;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Resolve a bare package specifier using the `output-workflow-bundle` export condition when present.
|
|
142
|
+
*
|
|
143
|
+
* This keeps AST workflow import resolution aligned with webpack's configured condition order before
|
|
144
|
+
* falling back to Node's `require.resolve`.
|
|
145
|
+
*
|
|
146
|
+
* @param {string} fromAbsoluteFile - Absolute path to the importing module.
|
|
147
|
+
* @param {string} specifier - Bare npm package specifier.
|
|
148
|
+
* @returns {string|null} Absolute module path selected from package exports, or null when unsupported.
|
|
149
|
+
*/
|
|
150
|
+
const resolveBareSpecifierWithOutputCondition = ( fromAbsoluteFile, specifier ) => {
|
|
151
|
+
const { packageName, subpath } = packagePartsFromSpecifier( specifier );
|
|
152
|
+
const pkgJsonPath = findPackageJson( dirname( fromAbsoluteFile ), packageName );
|
|
153
|
+
if ( !pkgJsonPath ) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const pkgRoot = dirname( pkgJsonPath );
|
|
158
|
+
const pkg = ( () => {
|
|
159
|
+
try {
|
|
160
|
+
return JSON.parse( readFileSync( pkgJsonPath, 'utf8' ) );
|
|
161
|
+
} catch {
|
|
162
|
+
return {};
|
|
163
|
+
}
|
|
164
|
+
} )();
|
|
165
|
+
const exportsIsRootConditionMap = pkg.exports && typeof pkg.exports === 'object' && !Array.isArray( pkg.exports ) &&
|
|
166
|
+
Object.keys( pkg.exports ).every( key => !key.startsWith( '.' ) );
|
|
167
|
+
const exportTarget = ( () => {
|
|
168
|
+
if ( typeof pkg.exports === 'string' && subpath === '.' ) {
|
|
169
|
+
return pkg.exports;
|
|
170
|
+
}
|
|
171
|
+
if ( subpath === '.' && exportsIsRootConditionMap ) {
|
|
172
|
+
return pkg.exports;
|
|
173
|
+
}
|
|
174
|
+
if ( pkg.exports && typeof pkg.exports === 'object' ) {
|
|
175
|
+
return pkg.exports[subpath];
|
|
176
|
+
}
|
|
177
|
+
return null;
|
|
178
|
+
} )();
|
|
179
|
+
const conditionTarget = resolveConditionalExportTarget( exportTarget );
|
|
180
|
+
return conditionTarget ? resolvePath( pkgRoot, conditionTarget ) : null;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* @param {import('@babel/types').Identifier | import('@babel/types').StringLiteral} node
|
|
185
|
+
* @returns {string}
|
|
186
|
+
*/
|
|
187
|
+
const exportSpecifierName = node => ( isIdentifier( node ) ? node.name : node.value );
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Resolve a named export by following `export { ... } from` and `export * from` chains until
|
|
191
|
+
* a `workflow.js` module is reached, then read the workflow runtime name via {@link buildWorkflowNameMap}.
|
|
192
|
+
*
|
|
193
|
+
* @param {string} moduleAbsPath - Absolute path to the current module file.
|
|
194
|
+
* @param {string} soughtExportedName - Public export name (`'default'` for default export).
|
|
195
|
+
* @param {Set<string>} visited - Cycle guard keys `path::exportName`.
|
|
196
|
+
* @param {Map<string, {default: (string|null), named: Map<string,string>}>} workflowNameCache
|
|
197
|
+
* @returns {string|null} Declared workflow `name` or null if not resolved as a workflow.
|
|
198
|
+
*/
|
|
199
|
+
const resolveNamedExportThroughReexports = (
|
|
200
|
+
moduleAbsPath, soughtExportedName, visited, workflowNameCache
|
|
201
|
+
) => {
|
|
202
|
+
const visitKey = `${moduleAbsPath}::${soughtExportedName}`;
|
|
203
|
+
if ( visited.has( visitKey ) ) {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
visited.add( visitKey );
|
|
207
|
+
|
|
208
|
+
if ( absolutePathIsWorkflowJsFile( moduleAbsPath ) ) {
|
|
209
|
+
const wfMap = buildWorkflowNameMap( moduleAbsPath, workflowNameCache );
|
|
210
|
+
if ( soughtExportedName === 'default' ) {
|
|
211
|
+
return wfMap.default;
|
|
212
|
+
}
|
|
213
|
+
return wfMap.named.get( soughtExportedName ) ?? null;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const ast = ( () => {
|
|
217
|
+
try {
|
|
218
|
+
return parse( readFileSync( moduleAbsPath, 'utf8' ), moduleAbsPath );
|
|
219
|
+
} catch {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
} )();
|
|
223
|
+
if ( !ast ) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
const moduleDir = dirname( moduleAbsPath );
|
|
227
|
+
|
|
228
|
+
for ( const node of ast.program.body ) {
|
|
229
|
+
if ( !isExportNamedDeclaration( node ) || !node.source ) {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
const targetAbs = resolvePath( moduleDir, node.source.value );
|
|
233
|
+
for ( const spec of node.specifiers ) {
|
|
234
|
+
if ( !isExportSpecifier( spec ) ) {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
const exported = exportSpecifierName( spec.exported );
|
|
238
|
+
if ( exported !== soughtExportedName ) {
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
const remoteName = exportSpecifierName( spec.local );
|
|
242
|
+
const inner = remoteName === 'default' ? 'default' : remoteName;
|
|
243
|
+
const resolved = resolveNamedExportThroughReexports(
|
|
244
|
+
targetAbs, inner, visited, workflowNameCache
|
|
245
|
+
);
|
|
246
|
+
if ( resolved ) {
|
|
247
|
+
return resolved;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
for ( const node of ast.program.body ) {
|
|
253
|
+
if ( !isExportAllDeclaration( node ) || !isStringLiteral( node.source ) ) {
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
const targetAbs = resolvePath( moduleDir, node.source.value );
|
|
257
|
+
const resolved = resolveNamedExportThroughReexports(
|
|
258
|
+
targetAbs, soughtExportedName, visited, workflowNameCache
|
|
259
|
+
);
|
|
260
|
+
if ( resolved ) {
|
|
261
|
+
return resolved;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return null;
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Returns true when a module is itself a workflow.js file or re-exports a module that is.
|
|
270
|
+
*
|
|
271
|
+
* @param {string} moduleAbsPath
|
|
272
|
+
* @param {Set<string>} [visited]
|
|
273
|
+
* @returns {boolean}
|
|
274
|
+
*/
|
|
275
|
+
const moduleMayExportWorkflows = ( moduleAbsPath, visited = new Set() ) => {
|
|
276
|
+
if ( visited.has( moduleAbsPath ) ) {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
visited.add( moduleAbsPath );
|
|
280
|
+
if ( absolutePathIsWorkflowJsFile( moduleAbsPath ) ) {
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
const ast = ( () => {
|
|
284
|
+
try {
|
|
285
|
+
return parse( readFileSync( moduleAbsPath, 'utf8' ), moduleAbsPath );
|
|
286
|
+
} catch {
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
} )();
|
|
290
|
+
if ( !ast ) {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
const moduleDir = dirname( moduleAbsPath );
|
|
294
|
+
for ( const node of ast.program.body ) {
|
|
295
|
+
if ( isExportAllDeclaration( node ) && isStringLiteral( node.source ) ) {
|
|
296
|
+
if ( moduleMayExportWorkflows( resolvePath( moduleDir, node.source.value ), visited ) ) {
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if ( isExportNamedDeclaration( node ) && isStringLiteral( node.source ) ) {
|
|
301
|
+
if ( moduleMayExportWorkflows( resolvePath( moduleDir, node.source.value ), visited ) ) {
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return false;
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Resolve the package entry (or subpath) for `specifier` from `fromAbsoluteFile`, then follow
|
|
311
|
+
* re-exports until `workflow.js` is reached.
|
|
312
|
+
*
|
|
313
|
+
* @param {string} fromAbsoluteFile - Absolute path to the importing file (e.g. `workflow.js`).
|
|
314
|
+
* @param {string} specifier - Bare npm specifier or subpath import.
|
|
315
|
+
* @returns {string|null} Absolute path to the resolved entry file, or null on failure.
|
|
316
|
+
*/
|
|
317
|
+
const resolveBareSpecifierToFirstModule = ( fromAbsoluteFile, specifier ) => {
|
|
318
|
+
const conditionalEntry = resolveBareSpecifierWithOutputCondition( fromAbsoluteFile, specifier );
|
|
319
|
+
if ( conditionalEntry ) {
|
|
320
|
+
return conditionalEntry;
|
|
321
|
+
}
|
|
322
|
+
try {
|
|
323
|
+
const req = createRequire( pathToFileURL( fromAbsoluteFile ).href );
|
|
324
|
+
return req.resolve( specifier );
|
|
325
|
+
} catch {
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* For each ESM import specifier from a bare npm module, resolve the bound workflow runtime name.
|
|
332
|
+
* All specifiers must resolve as workflows; otherwise returns `none` (caller leaves the import alone)
|
|
333
|
+
* or `partial` (caller should throw).
|
|
334
|
+
*
|
|
335
|
+
* @param {object} params
|
|
336
|
+
* @param {string} params.fromAbsoluteFile - Absolute path to the importing `workflow.js`.
|
|
337
|
+
* @param {string} params.specifier - Bare npm import source.
|
|
338
|
+
* @param {readonly import('@babel/types').ImportDeclaration['specifiers']} params.specifiers
|
|
339
|
+
* @param {Map<string, {default: (string|null), named: Map<string,string>}>} params.workflowNameCache
|
|
340
|
+
* @returns {{ type: 'all', bindings: Array<{ localName: string, workflowName: string }> } |
|
|
341
|
+
* { type: 'none' } | { type: 'partial' }}
|
|
342
|
+
* @throws {Error} When a namespace import targets a workflow package.
|
|
343
|
+
*/
|
|
344
|
+
export const resolveBareImportSpecifiersAsWorkflows = ( {
|
|
345
|
+
fromAbsoluteFile,
|
|
346
|
+
specifier,
|
|
347
|
+
specifiers,
|
|
348
|
+
workflowNameCache
|
|
349
|
+
} ) => {
|
|
350
|
+
const entry = resolveBareSpecifierToFirstModule( fromAbsoluteFile, specifier );
|
|
351
|
+
if ( !entry ) {
|
|
352
|
+
return { type: 'none' };
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if ( specifiers.some( isImportNamespaceSpecifier ) ) {
|
|
356
|
+
if ( moduleMayExportWorkflows( entry ) ) {
|
|
357
|
+
throw unsupportedNamespaceWorkflowImportError( specifier );
|
|
358
|
+
}
|
|
359
|
+
return { type: 'none' };
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const rows = [];
|
|
363
|
+
const visited = new Set();
|
|
364
|
+
|
|
365
|
+
for ( const sp of specifiers ) {
|
|
366
|
+
const binding = ( () => {
|
|
367
|
+
if ( isImportDefaultSpecifier( sp ) ) {
|
|
368
|
+
return { soughtExport: 'default', localName: sp.local.name };
|
|
369
|
+
}
|
|
370
|
+
if ( isImportSpecifier( sp ) ) {
|
|
371
|
+
return { soughtExport: sp.imported.name, localName: sp.local.name };
|
|
372
|
+
}
|
|
373
|
+
return null;
|
|
374
|
+
} )();
|
|
375
|
+
if ( !binding ) {
|
|
376
|
+
return { type: 'none' };
|
|
377
|
+
}
|
|
378
|
+
const { soughtExport, localName } = binding;
|
|
379
|
+
|
|
380
|
+
const workflowName = resolveNamedExportThroughReexports(
|
|
381
|
+
entry, soughtExport, visited, workflowNameCache
|
|
382
|
+
);
|
|
383
|
+
rows.push( { localName, workflowName } );
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const resolvedCount = rows.filter( r => r.workflowName ).length;
|
|
387
|
+
if ( resolvedCount === 0 ) {
|
|
388
|
+
return { type: 'none' };
|
|
389
|
+
}
|
|
390
|
+
if ( resolvedCount !== rows.length ) {
|
|
391
|
+
return { type: 'partial' };
|
|
392
|
+
}
|
|
393
|
+
return { type: 'all', bindings: rows };
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Resolves `const { a, b } = require('bare')` the same way as ESM imports (all keys must be workflows).
|
|
398
|
+
*
|
|
399
|
+
* @param {object} params
|
|
400
|
+
* @param {string} params.fromAbsoluteFile
|
|
401
|
+
* @param {string} params.specifier
|
|
402
|
+
* @param {readonly import('@babel/types').ObjectPattern['properties']} params.properties
|
|
403
|
+
* @param {Map<string, {default: (string|null), named: Map<string,string>}>} params.workflowNameCache
|
|
404
|
+
* @returns {{ type: 'all', bindings: Array<{ localName: string, workflowName: string }> } |
|
|
405
|
+
* { type: 'none' } | { type: 'partial' }}
|
|
406
|
+
*/
|
|
407
|
+
export const resolveBareDestructuredRequireAsWorkflows = ( {
|
|
408
|
+
fromAbsoluteFile,
|
|
409
|
+
specifier,
|
|
410
|
+
properties,
|
|
411
|
+
workflowNameCache
|
|
412
|
+
} ) => {
|
|
413
|
+
const entry = resolveBareSpecifierToFirstModule( fromAbsoluteFile, specifier );
|
|
414
|
+
if ( !entry ) {
|
|
415
|
+
return { type: 'none' };
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const rows = [];
|
|
419
|
+
const visited = new Set();
|
|
420
|
+
|
|
421
|
+
for ( const prop of properties ) {
|
|
422
|
+
if ( prop.type !== 'ObjectProperty' || !isIdentifier( prop.key ) ) {
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
const importedName = prop.key.name;
|
|
426
|
+
const val = prop.value;
|
|
427
|
+
const localName = isIdentifier( val ) ? val.name : null;
|
|
428
|
+
if ( !localName ) {
|
|
429
|
+
return { type: 'partial' };
|
|
430
|
+
}
|
|
431
|
+
const workflowName = resolveNamedExportThroughReexports(
|
|
432
|
+
entry, importedName, visited, workflowNameCache
|
|
433
|
+
);
|
|
434
|
+
rows.push( { localName, workflowName } );
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if ( rows.length === 0 ) {
|
|
438
|
+
return { type: 'none' };
|
|
439
|
+
}
|
|
440
|
+
const resolvedCount = rows.filter( r => r.workflowName ).length;
|
|
441
|
+
if ( resolvedCount === 0 ) {
|
|
442
|
+
return { type: 'none' };
|
|
443
|
+
}
|
|
444
|
+
if ( resolvedCount !== rows.length ) {
|
|
445
|
+
return { type: 'partial' };
|
|
446
|
+
}
|
|
447
|
+
return { type: 'all', bindings: rows };
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Default `require('bare')` as a single default workflow binding.
|
|
452
|
+
*
|
|
453
|
+
* @param {string} fromAbsoluteFile
|
|
454
|
+
* @param {string} specifier
|
|
455
|
+
* @param {string} localName
|
|
456
|
+
* @param {Map<string, {default: (string|null), named: Map<string,string>}>} workflowNameCache
|
|
457
|
+
* @returns {{ type: 'binding', localName: string, workflowName: string } | { type: 'none' } | { type: 'partial' }}
|
|
458
|
+
*/
|
|
459
|
+
export const resolveBareDefaultRequireAsWorkflow = (
|
|
460
|
+
fromAbsoluteFile, specifier, localName, workflowNameCache
|
|
461
|
+
) => {
|
|
462
|
+
const entry = resolveBareSpecifierToFirstModule( fromAbsoluteFile, specifier );
|
|
463
|
+
if ( !entry ) {
|
|
464
|
+
return { type: 'none' };
|
|
465
|
+
}
|
|
466
|
+
const visited = new Set();
|
|
467
|
+
const workflowName = resolveNamedExportThroughReexports(
|
|
468
|
+
entry, 'default', visited, workflowNameCache
|
|
469
|
+
);
|
|
470
|
+
if ( !workflowName ) {
|
|
471
|
+
return { type: 'none' };
|
|
472
|
+
}
|
|
473
|
+
return { type: 'binding', localName, workflowName };
|
|
474
|
+
};
|