@auraindustry/aurajs 0.1.3 → 0.1.5
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 +7 -0
- package/benchmarks/perf-thresholds.json +27 -0
- package/package.json +6 -1
- package/src/ai-guidance.mjs +302 -0
- package/src/authored-project.mjs +498 -2
- package/src/build-contract/capabilities.mjs +87 -1
- package/src/build-contract/constants.mjs +1 -0
- package/src/build-contract.mjs +2 -0
- package/src/bundler.mjs +143 -13
- package/src/cli.mjs +681 -13
- package/src/commands/packs.mjs +741 -0
- package/src/commands/project-authoring.mjs +128 -1
- package/src/conformance/cases/app-and-ui-runtime-cases.mjs +1 -2
- package/src/conformance/cases/core-runtime-cases.mjs +6 -2
- package/src/conformance/cases/scene3d-and-media-cases.mjs +238 -0
- package/src/conformance/cases/systems-and-gameplay-cases.mjs +265 -4
- package/src/conformance-mobile.mjs +166 -0
- package/src/conformance.mjs +89 -30
- package/src/evidence-bundle.mjs +242 -0
- package/src/headless-test/runtime-coordinator.mjs +186 -33
- package/src/headless-test.mjs +2 -0
- package/src/helpers/2d/index.mjs +183 -0
- package/src/helpers/index.mjs +26 -0
- package/src/helpers/starter-utils/adventure-objectives.js +102 -0
- package/src/helpers/starter-utils/adventure-world-2d.js +221 -0
- package/src/helpers/starter-utils/animation-2d.js +337 -0
- package/src/helpers/starter-utils/animation-packaging-2d.js +203 -0
- package/src/helpers/starter-utils/atlas-assets-2d.js +111 -0
- package/src/helpers/starter-utils/autoplay-debug-2d.js +215 -0
- package/src/helpers/starter-utils/avatar-3d.js +404 -0
- package/src/helpers/starter-utils/combat-feedback-2d.js +320 -0
- package/src/helpers/starter-utils/combat-runtime-2d.js +290 -0
- package/src/helpers/starter-utils/core.js +150 -0
- package/src/helpers/starter-utils/dialogue-2d.js +351 -0
- package/src/helpers/starter-utils/enemy-archetypes-2d.js +68 -0
- package/src/helpers/starter-utils/index.js +26 -0
- package/src/helpers/starter-utils/inventory-2d.js +268 -0
- package/src/helpers/starter-utils/journal-2d.js +267 -0
- package/src/helpers/starter-utils/platformer-3d.js +132 -0
- package/src/helpers/starter-utils/scene-audio-2d.js +236 -0
- package/src/helpers/starter-utils/streamed-world-2d.js +378 -0
- package/src/helpers/starter-utils/tilemap-nav-2d.js +499 -0
- package/src/helpers/starter-utils/tilemap-world-2d.js +205 -0
- package/src/helpers/starter-utils/triggers.js +662 -0
- package/src/helpers/starter-utils/tween-2d.js +615 -0
- package/src/helpers/starter-utils/wave-director.js +101 -0
- package/src/helpers/starter-utils/world-compositor-2d.js +253 -0
- package/src/helpers/starter-utils/world-persistence-2d.js +180 -0
- package/src/mobile/android/build.mjs +606 -0
- package/src/mobile/android/host-artifact.mjs +280 -0
- package/src/mobile/ios/build.mjs +1323 -0
- package/src/mobile/ios/host-artifact.mjs +819 -0
- package/src/mobile/shared/capabilities.mjs +174 -0
- package/src/packs/catalog.mjs +259 -0
- package/src/perf-benchmark-runner.mjs +17 -12
- package/src/perf-benchmark.mjs +408 -4
- package/src/publish-command.mjs +303 -6
- package/src/replay-runtime.mjs +257 -0
- package/src/scaffold/config.mjs +2 -0
- package/src/scaffold/fs.mjs +8 -1
- package/src/scaffold/project-docs.mjs +43 -1
- package/src/scaffold.mjs +4 -0
- package/src/session-runtime.mjs +4 -3
- package/src/web-conformance.mjs +0 -36
- package/templates/create/2d-adventure/config/gameplay/adventure.config.js +9 -6
- package/templates/create/2d-adventure/content/gameplay/dialogue.js +85 -0
- package/templates/create/2d-adventure/content/gameplay/world.js +32 -36
- package/templates/create/2d-adventure/content/gameplay/world.tilemap.json +273 -0
- package/templates/create/2d-adventure/docs/design/loop.md +4 -3
- package/templates/create/2d-adventure/prefabs/relic.prefab.js +10 -10
- package/templates/create/2d-adventure/prefabs/world.prefab.js +127 -74
- package/templates/create/2d-adventure/scenes/gameplay.scene.js +603 -112
- package/templates/create/2d-adventure/src/runtime/capabilities.js +16 -0
- package/templates/create/2d-adventure/ui/hud.screen.js +187 -4
- package/templates/create/2d-adventure/ui/journal.screen.js +183 -0
- package/templates/create/3d/scenes/gameplay.scene.js +30 -3
- package/templates/create/3d/src/runtime/capabilities.js +5 -0
- package/templates/create/3d/src/runtime/materials.js +10 -0
- package/templates/create/3d-adventure/scenes/gameplay.scene.js +30 -3
- package/templates/create/3d-adventure/src/runtime/capabilities.js +5 -0
- package/templates/create/3d-adventure/src/runtime/materials.js +11 -0
- package/templates/create/3d-collectathon/scenes/gameplay.scene.js +30 -3
- package/templates/create/3d-collectathon/src/runtime/capabilities.js +5 -0
- package/templates/create/3d-collectathon/src/runtime/materials.js +10 -0
- package/templates/create/shared/src/runtime/ui-forms.js +552 -0
- package/templates/create/shared/src/starter-utils/adventure-world-2d.js +221 -0
- package/templates/create/shared/src/starter-utils/animation-packaging-2d.js +203 -0
- package/templates/create/shared/src/starter-utils/atlas-assets-2d.js +111 -0
- package/templates/create/shared/src/starter-utils/autoplay-debug-2d.js +215 -0
- package/templates/create/shared/src/starter-utils/combat-runtime-2d.js +290 -0
- package/templates/create/shared/src/starter-utils/dialogue-2d.js +351 -0
- package/templates/create/shared/src/starter-utils/index.js +15 -1
- package/templates/create/shared/src/starter-utils/inventory-2d.js +268 -0
- package/templates/create/shared/src/starter-utils/journal-2d.js +267 -0
- package/templates/create/shared/src/starter-utils/scene-audio-2d.js +236 -0
- package/templates/create/shared/src/starter-utils/streamed-world-2d.js +378 -0
- package/templates/create/shared/src/starter-utils/tilemap-nav-2d.js +499 -0
- package/templates/create/shared/src/starter-utils/tilemap-world-2d.js +205 -0
- package/templates/create/shared/src/starter-utils/world-compositor-2d.js +253 -0
- package/templates/create/shared/src/starter-utils/world-persistence-2d.js +180 -0
- package/templates/create-bin/play.js +36 -7
- package/templates/skills/auramaxx/SKILL.md +46 -0
- package/templates/skills/auramaxx/project-requirements.md +68 -0
- package/templates/skills/auramaxx/starter-recipes.md +104 -0
- package/templates/skills/auramaxx/validation-checklist.md +49 -0
- package/templates/skills/aurajs/SKILL.md +0 -96
- package/templates/skills/aurajs/api-contract-3d.md +0 -7
- package/templates/skills/aurajs/api-contract.md +0 -7
|
@@ -2,11 +2,44 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
+
MOBILE_CAPABILITY_REPORT_SCHEMA,
|
|
5
6
|
PROJECT_CAPABILITY_DECLARATION_FILE,
|
|
6
7
|
WEB_CAPABILITY_DECLARATION_SCHEMA,
|
|
7
8
|
WEB_RUNTIME_CONFIG_SCHEMA,
|
|
8
9
|
} from './constants.mjs';
|
|
9
10
|
import { normalizePositiveInt } from './helpers.mjs';
|
|
11
|
+
import { buildMobileCapabilityReport } from '../mobile/shared/capabilities.mjs';
|
|
12
|
+
|
|
13
|
+
const MOBILE_UNSUPPORTED_API_RULES = Object.freeze([
|
|
14
|
+
Object.freeze({
|
|
15
|
+
api: 'aura.window.setCursorLocked',
|
|
16
|
+
reasonCode: 'mobile_cursor_lock_unsupported',
|
|
17
|
+
}),
|
|
18
|
+
Object.freeze({
|
|
19
|
+
api: 'aura.input.getMouseDelta',
|
|
20
|
+
reasonCode: 'mobile_cursor_lock_unsupported',
|
|
21
|
+
}),
|
|
22
|
+
Object.freeze({
|
|
23
|
+
api: 'aura.input.getMouseWheel',
|
|
24
|
+
reasonCode: 'mobile_mouse_wheel_unsupported',
|
|
25
|
+
}),
|
|
26
|
+
Object.freeze({
|
|
27
|
+
api: 'aura.window.setSize',
|
|
28
|
+
reasonCode: 'mobile_window_management_unsupported',
|
|
29
|
+
}),
|
|
30
|
+
Object.freeze({
|
|
31
|
+
api: 'aura.window.setFullscreen',
|
|
32
|
+
reasonCode: 'mobile_window_management_unsupported',
|
|
33
|
+
}),
|
|
34
|
+
Object.freeze({
|
|
35
|
+
api: 'aura.window.setTitle',
|
|
36
|
+
reasonCode: 'mobile_window_management_unsupported',
|
|
37
|
+
}),
|
|
38
|
+
Object.freeze({
|
|
39
|
+
api: 'aura.window.setCursorVisible',
|
|
40
|
+
reasonCode: 'mobile_window_management_unsupported',
|
|
41
|
+
}),
|
|
42
|
+
]);
|
|
10
43
|
|
|
11
44
|
export function buildRuntimeConfig(options = {}) {
|
|
12
45
|
const windowConfig = options.windowConfig && typeof options.windowConfig === 'object'
|
|
@@ -67,6 +100,40 @@ function normalizeOptionalModules(modules) {
|
|
|
67
100
|
};
|
|
68
101
|
}
|
|
69
102
|
|
|
103
|
+
function buildMobileTargetAssessment(requiredApis) {
|
|
104
|
+
const matchedApis = new Set();
|
|
105
|
+
const reasonCodes = new Set();
|
|
106
|
+
|
|
107
|
+
for (const api of requiredApis) {
|
|
108
|
+
for (const rule of MOBILE_UNSUPPORTED_API_RULES) {
|
|
109
|
+
if (api !== rule.api) continue;
|
|
110
|
+
matchedApis.add(api);
|
|
111
|
+
reasonCodes.add(rule.reasonCode);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const normalizedMatchedApis = [...matchedApis].sort((a, b) => a.localeCompare(b));
|
|
116
|
+
const normalizedReasonCodes = [...reasonCodes].sort((a, b) => a.localeCompare(b));
|
|
117
|
+
const portable = normalizedReasonCodes.length === 0;
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
schema: 'aurajs.mobile-capability-assessment.v1',
|
|
121
|
+
portable,
|
|
122
|
+
matchedDesktopApis: normalizedMatchedApis,
|
|
123
|
+
unsupportedReasonCodes: normalizedReasonCodes,
|
|
124
|
+
targets: {
|
|
125
|
+
android: {
|
|
126
|
+
portable,
|
|
127
|
+
unsupportedReasonCodes: normalizedReasonCodes,
|
|
128
|
+
},
|
|
129
|
+
ios: {
|
|
130
|
+
portable,
|
|
131
|
+
unsupportedReasonCodes: normalizedReasonCodes,
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
70
137
|
function normalizeWebCapabilityDeclaration(value, baseModules = {}, source = null) {
|
|
71
138
|
const declaration = value && typeof value === 'object' && !Array.isArray(value)
|
|
72
139
|
? value
|
|
@@ -85,16 +152,18 @@ function normalizeWebCapabilityDeclaration(value, baseModules = {}, source = nul
|
|
|
85
152
|
}
|
|
86
153
|
|
|
87
154
|
const declaredModules = normalizeOptionalModules(declaration.optionalModules);
|
|
155
|
+
const requiredApis = normalizeRequiredApis(declaration.requiredApis);
|
|
88
156
|
return {
|
|
89
157
|
schema: WEB_CAPABILITY_DECLARATION_SCHEMA,
|
|
90
158
|
source,
|
|
91
|
-
requiredApis
|
|
159
|
+
requiredApis,
|
|
92
160
|
optionalModules: {
|
|
93
161
|
physics: baseModules.physics || declaredModules.physics,
|
|
94
162
|
network: baseModules.network || declaredModules.network,
|
|
95
163
|
multiplayer: baseModules.multiplayer || declaredModules.multiplayer,
|
|
96
164
|
steam: baseModules.steam || declaredModules.steam,
|
|
97
165
|
},
|
|
166
|
+
mobileTargetAssessment: buildMobileTargetAssessment(requiredApis),
|
|
98
167
|
};
|
|
99
168
|
}
|
|
100
169
|
|
|
@@ -114,3 +183,20 @@ export function readProjectCapabilityDeclaration({ projectRoot, modules }) {
|
|
|
114
183
|
|
|
115
184
|
return normalizeWebCapabilityDeclaration(parsed, baseModules, PROJECT_CAPABILITY_DECLARATION_FILE);
|
|
116
185
|
}
|
|
186
|
+
|
|
187
|
+
export function buildMobileCapabilityAssertion({ projectRoot, modules, target }) {
|
|
188
|
+
const capabilityDeclaration = readProjectCapabilityDeclaration({
|
|
189
|
+
projectRoot,
|
|
190
|
+
modules,
|
|
191
|
+
});
|
|
192
|
+
const report = buildMobileCapabilityReport({
|
|
193
|
+
target,
|
|
194
|
+
capabilityDeclaration,
|
|
195
|
+
});
|
|
196
|
+
return {
|
|
197
|
+
...report,
|
|
198
|
+
schema: MOBILE_CAPABILITY_REPORT_SCHEMA,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export { buildMobileCapabilityReport } from '../mobile/shared/capabilities.mjs';
|
|
@@ -2,5 +2,6 @@ export const BUILD_MANIFEST_SCHEMA = 'aurajs.build-manifest.v1';
|
|
|
2
2
|
export const WEB_BUILD_MANIFEST_SCHEMA = 'aurajs.web-build-manifest.v1';
|
|
3
3
|
export const WEB_RUNTIME_CONFIG_SCHEMA = 'aurajs.web-runtime-config.v1';
|
|
4
4
|
export const WEB_CAPABILITY_DECLARATION_SCHEMA = 'aurajs.web-capability-declaration.v1';
|
|
5
|
+
export const MOBILE_CAPABILITY_REPORT_SCHEMA = 'aurajs.mobile-capability-report.v1';
|
|
5
6
|
|
|
6
7
|
export const PROJECT_CAPABILITY_DECLARATION_FILE = 'aura.capabilities.json';
|
package/src/build-contract.mjs
CHANGED
|
@@ -16,11 +16,13 @@ import { WEB_INDEX_HTML, WEB_LOADER_SOURCE } from './build-contract/web-template
|
|
|
16
16
|
|
|
17
17
|
export {
|
|
18
18
|
BUILD_MANIFEST_SCHEMA,
|
|
19
|
+
MOBILE_CAPABILITY_REPORT_SCHEMA,
|
|
19
20
|
WEB_BUILD_MANIFEST_SCHEMA,
|
|
20
21
|
WEB_CAPABILITY_DECLARATION_SCHEMA,
|
|
21
22
|
WEB_RUNTIME_CONFIG_SCHEMA,
|
|
22
23
|
} from './build-contract/constants.mjs';
|
|
23
24
|
export { writeCanonicalJsonFile } from './build-contract/helpers.mjs';
|
|
25
|
+
export { buildRuntimeConfig, readProjectCapabilityDeclaration } from './build-contract/capabilities.mjs';
|
|
24
26
|
|
|
25
27
|
export function writeBuildManifest(options = {}) {
|
|
26
28
|
const outRoot = resolve(options.outRoot || process.cwd());
|
package/src/bundler.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
3
4
|
import {
|
|
4
5
|
existsSync,
|
|
5
6
|
mkdirSync,
|
|
@@ -9,7 +10,8 @@ import {
|
|
|
9
10
|
readdirSync,
|
|
10
11
|
watch,
|
|
11
12
|
} from 'node:fs';
|
|
12
|
-
import { dirname, isAbsolute, join, normalize, relative, resolve, sep } from 'node:path';
|
|
13
|
+
import { dirname, extname, isAbsolute, join, normalize, relative, resolve, sep } from 'node:path';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
13
15
|
import { buildGameActionRuntimeBootstrapSource } from './game-action-runtime.mjs';
|
|
14
16
|
import { buildStateRestoreRuntimeBootstrapSource } from './game-state-runtime.mjs';
|
|
15
17
|
|
|
@@ -42,6 +44,22 @@ const AUTHORED_SOURCE_ROOT_DIRS = Object.freeze([
|
|
|
42
44
|
'data',
|
|
43
45
|
]);
|
|
44
46
|
|
|
47
|
+
const BUNDLER_DIR = dirname(fileURLToPath(import.meta.url));
|
|
48
|
+
const CLI_PACKAGE_ROOT = resolve(BUNDLER_DIR, '..');
|
|
49
|
+
const CLI_PACKAGE = JSON.parse(readFileSync(resolve(CLI_PACKAGE_ROOT, 'package.json'), 'utf8'));
|
|
50
|
+
const CLI_PACKAGE_NAME = CLI_PACKAGE.name;
|
|
51
|
+
const SUPPORTED_HELPER_SPECIFIER = `${CLI_PACKAGE_NAME}/helpers`;
|
|
52
|
+
const SELF_PACKAGE_EXPORTS = new Map(
|
|
53
|
+
Object.entries(CLI_PACKAGE.exports || {})
|
|
54
|
+
.filter(([, target]) => typeof target === 'string' && target.length > 0)
|
|
55
|
+
.map(([subpath, target]) => {
|
|
56
|
+
const packageSpecifier = subpath === '.'
|
|
57
|
+
? CLI_PACKAGE_NAME
|
|
58
|
+
: `${CLI_PACKAGE_NAME}/${subpath.replace(/^\.\//, '')}`;
|
|
59
|
+
return [packageSpecifier, resolve(CLI_PACKAGE_ROOT, target)];
|
|
60
|
+
}),
|
|
61
|
+
);
|
|
62
|
+
|
|
45
63
|
export function isBundleError(error) {
|
|
46
64
|
return error instanceof BundleError;
|
|
47
65
|
}
|
|
@@ -78,6 +96,13 @@ export function formatBundleError(error, projectRoot = process.cwd()) {
|
|
|
78
96
|
}
|
|
79
97
|
}
|
|
80
98
|
|
|
99
|
+
if (Array.isArray(details.hints) && details.hints.length > 0) {
|
|
100
|
+
parts.push('hints:');
|
|
101
|
+
for (const hint of details.hints) {
|
|
102
|
+
parts.push(` - ${hint}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
81
106
|
if (details.cycle) {
|
|
82
107
|
const rendered = details.cycle.map((entry) => relPath(projectRoot, entry)).join(' -> ');
|
|
83
108
|
parts.push(`cycle: ${rendered}`);
|
|
@@ -247,6 +272,7 @@ function buildModuleGraph(ctx) {
|
|
|
247
272
|
filePath,
|
|
248
273
|
code: parsed.code,
|
|
249
274
|
imports: dependencies,
|
|
275
|
+
moduleType: parsed.moduleType,
|
|
250
276
|
});
|
|
251
277
|
|
|
252
278
|
state.set(filePath, 2);
|
|
@@ -298,6 +324,10 @@ function emitBundle(graph) {
|
|
|
298
324
|
}
|
|
299
325
|
|
|
300
326
|
function transformModule(moduleMeta, srcDir) {
|
|
327
|
+
if (moduleMeta.moduleType === 'json') {
|
|
328
|
+
return `__exports.default = ${moduleMeta.code.trim()};`;
|
|
329
|
+
}
|
|
330
|
+
|
|
301
331
|
const importByLine = new Map();
|
|
302
332
|
const importContinuationLines = new Set();
|
|
303
333
|
for (const item of moduleMeta.imports) {
|
|
@@ -483,15 +513,23 @@ function readParsedModule(ctx, filePath) {
|
|
|
483
513
|
const content = readFileSync(filePath, 'utf8');
|
|
484
514
|
const hash = sha1(content);
|
|
485
515
|
const cached = ctx.cache.files.get(filePath);
|
|
516
|
+
const moduleType = extname(filePath).toLowerCase() === '.json' ? 'json' : 'js';
|
|
486
517
|
|
|
487
518
|
if (cached && cached.hash === hash) {
|
|
488
519
|
return cached;
|
|
489
520
|
}
|
|
490
521
|
|
|
522
|
+
if (moduleType === 'json') {
|
|
523
|
+
validateJsonModule(content, filePath);
|
|
524
|
+
const parsed = { hash, code: content, imports: [], moduleType };
|
|
525
|
+
ctx.cache.files.set(filePath, parsed);
|
|
526
|
+
return parsed;
|
|
527
|
+
}
|
|
528
|
+
|
|
491
529
|
validateSyntax(content, filePath);
|
|
492
530
|
|
|
493
531
|
const imports = scanImports(content, filePath);
|
|
494
|
-
const parsed = { hash, code: content, imports };
|
|
532
|
+
const parsed = { hash, code: content, imports, moduleType };
|
|
495
533
|
ctx.cache.files.set(filePath, parsed);
|
|
496
534
|
return parsed;
|
|
497
535
|
}
|
|
@@ -559,7 +597,8 @@ function scanImports(code, filePath) {
|
|
|
559
597
|
|
|
560
598
|
function parseImportStatement(statement) {
|
|
561
599
|
const normalized = statement.replace(/\s+/g, ' ').trim();
|
|
562
|
-
const
|
|
600
|
+
const importAttributes = String.raw`(?:\s+(?:with|assert)\s+\{[^}]*\})?`;
|
|
601
|
+
const sideEffect = normalized.match(new RegExp(`^import\\s+['"]([^'"\\n]+)['"]${importAttributes}\\s*;?$`));
|
|
563
602
|
if (sideEffect) {
|
|
564
603
|
return {
|
|
565
604
|
clause: null,
|
|
@@ -567,7 +606,7 @@ function parseImportStatement(statement) {
|
|
|
567
606
|
};
|
|
568
607
|
}
|
|
569
608
|
|
|
570
|
-
const fromImport = normalized.match(
|
|
609
|
+
const fromImport = normalized.match(new RegExp(`^import\\s+(.+?)\\s+from\\s+['"]([^'"\\n]+)['"]${importAttributes}\\s*;?$`));
|
|
571
610
|
if (fromImport) {
|
|
572
611
|
return {
|
|
573
612
|
clause: fromImport[1].trim(),
|
|
@@ -582,15 +621,12 @@ function resolveSpecifier(projectRoot, allowedSourceRoots, srcDir, importer, imp
|
|
|
582
621
|
const specifier = importEntry.specifier;
|
|
583
622
|
|
|
584
623
|
if (!(specifier.startsWith('./') || specifier.startsWith('../'))) {
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
specifier,
|
|
592
|
-
line: importEntry.line,
|
|
593
|
-
},
|
|
624
|
+
return resolveSupportedPackageSpecifier(
|
|
625
|
+
projectRoot,
|
|
626
|
+
allowedSourceRoots,
|
|
627
|
+
srcDir,
|
|
628
|
+
importer,
|
|
629
|
+
importEntry,
|
|
594
630
|
);
|
|
595
631
|
}
|
|
596
632
|
|
|
@@ -623,6 +659,60 @@ function resolveSpecifier(projectRoot, allowedSourceRoots, srcDir, importer, imp
|
|
|
623
659
|
);
|
|
624
660
|
}
|
|
625
661
|
|
|
662
|
+
function resolveSupportedPackageSpecifier(projectRoot, allowedSourceRoots, srcDir, importer, importEntry) {
|
|
663
|
+
const specifier = importEntry.specifier;
|
|
664
|
+
let resolved = null;
|
|
665
|
+
let packageRoot = null;
|
|
666
|
+
|
|
667
|
+
if (specifier === CLI_PACKAGE_NAME || specifier.startsWith(`${CLI_PACKAGE_NAME}/`)) {
|
|
668
|
+
try {
|
|
669
|
+
resolved = createRequire(importer).resolve(specifier);
|
|
670
|
+
} catch {
|
|
671
|
+
resolved = null;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
if (!resolved && SELF_PACKAGE_EXPORTS.has(specifier)) {
|
|
675
|
+
resolved = SELF_PACKAGE_EXPORTS.get(specifier);
|
|
676
|
+
packageRoot = CLI_PACKAGE_ROOT;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
if (!resolved) {
|
|
681
|
+
throw new BundleError(
|
|
682
|
+
BUNDLE_ERROR_CODES.UNSUPPORTED_SPECIFIER,
|
|
683
|
+
`Unsupported module specifier "${specifier}".`,
|
|
684
|
+
{
|
|
685
|
+
file: importer,
|
|
686
|
+
importer,
|
|
687
|
+
specifier,
|
|
688
|
+
line: importEntry.line,
|
|
689
|
+
hints: [
|
|
690
|
+
'Use relative imports for authored project files.',
|
|
691
|
+
`Use the local src/starter-utils copy or supported package imports from "${SUPPORTED_HELPER_SPECIFIER}".`,
|
|
692
|
+
],
|
|
693
|
+
},
|
|
694
|
+
);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
if (!(existsSync(resolved) && statSync(resolved).isFile())) {
|
|
698
|
+
throw new BundleError(
|
|
699
|
+
BUNDLE_ERROR_CODES.MODULE_NOT_FOUND,
|
|
700
|
+
`Module not found for specifier "${specifier}".`,
|
|
701
|
+
{
|
|
702
|
+
file: importer,
|
|
703
|
+
importer,
|
|
704
|
+
specifier,
|
|
705
|
+
line: importEntry.line,
|
|
706
|
+
candidates: [relPath(srcDir, resolved)],
|
|
707
|
+
},
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
packageRoot ||= findNearestPackageRoot(resolved) || CLI_PACKAGE_ROOT;
|
|
712
|
+
appendAllowedRoot(allowedSourceRoots, packageRoot);
|
|
713
|
+
return resolved;
|
|
714
|
+
}
|
|
715
|
+
|
|
626
716
|
function resolveAllowedSourceRoots(projectRoot, explicitRoots = null) {
|
|
627
717
|
if (Array.isArray(explicitRoots) && explicitRoots.length > 0) {
|
|
628
718
|
return explicitRoots
|
|
@@ -640,6 +730,28 @@ function resolveAllowedSourceRoots(projectRoot, explicitRoots = null) {
|
|
|
640
730
|
return roots;
|
|
641
731
|
}
|
|
642
732
|
|
|
733
|
+
function appendAllowedRoot(allowedSourceRoots, root) {
|
|
734
|
+
const normalizedRoot = resolve(root);
|
|
735
|
+
if (!allowedSourceRoots.some((candidate) => candidate === normalizedRoot)) {
|
|
736
|
+
allowedSourceRoots.push(normalizedRoot);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
function findNearestPackageRoot(filePath) {
|
|
741
|
+
let current = dirname(filePath);
|
|
742
|
+
while (true) {
|
|
743
|
+
const packageJsonPath = join(current, 'package.json');
|
|
744
|
+
if (existsSync(packageJsonPath) && statSync(packageJsonPath).isFile()) {
|
|
745
|
+
return current;
|
|
746
|
+
}
|
|
747
|
+
const parent = resolve(current, '..');
|
|
748
|
+
if (parent === current) {
|
|
749
|
+
return null;
|
|
750
|
+
}
|
|
751
|
+
current = parent;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
643
755
|
function ensureEntryWithinSource(projectRoot, allowedSourceRoots, entryFile) {
|
|
644
756
|
if (allowedSourceRoots.some((root) => pathIsWithinRoot(root, entryFile))) {
|
|
645
757
|
return;
|
|
@@ -651,6 +763,10 @@ function ensureEntryWithinSource(projectRoot, allowedSourceRoots, entryFile) {
|
|
|
651
763
|
{
|
|
652
764
|
file: entryFile,
|
|
653
765
|
candidates: allowedSourceRoots.map((root) => relPath(projectRoot, root)),
|
|
766
|
+
hints: [
|
|
767
|
+
'Entrypoints must stay inside the authored project source roots.',
|
|
768
|
+
`Use "${SUPPORTED_HELPER_SPECIFIER}" through local src/starter-utils copies for shared helper code instead of moving entrypoints outside src/.`,
|
|
769
|
+
],
|
|
654
770
|
},
|
|
655
771
|
);
|
|
656
772
|
}
|
|
@@ -674,6 +790,10 @@ function ensureWithinSource(projectRoot, allowedSourceRoots, candidate, importer
|
|
|
674
790
|
specifier: importEntry.specifier,
|
|
675
791
|
line: importEntry.line,
|
|
676
792
|
candidates: allowedSourceRoots.map((root) => relPath(projectRoot, root)),
|
|
793
|
+
hints: [
|
|
794
|
+
'Move shared authored helper code into src/ if it is project-specific.',
|
|
795
|
+
`For supported shared helper lanes, import from "${SUPPORTED_HELPER_SPECIFIER}" or keep the local src/starter-utils copy.`,
|
|
796
|
+
],
|
|
677
797
|
},
|
|
678
798
|
);
|
|
679
799
|
}
|
|
@@ -710,6 +830,16 @@ function validateSyntax(code, filePath) {
|
|
|
710
830
|
});
|
|
711
831
|
}
|
|
712
832
|
|
|
833
|
+
function validateJsonModule(code, filePath) {
|
|
834
|
+
try {
|
|
835
|
+
JSON.parse(code);
|
|
836
|
+
} catch {
|
|
837
|
+
throw new BundleError(BUNDLE_ERROR_CODES.SYNTAX_ERROR, 'Invalid JSON module.', {
|
|
838
|
+
file: filePath,
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
713
843
|
function hasDynamicImport(code) {
|
|
714
844
|
return /(^|[^\w$.])import\s*\(/m.test(code);
|
|
715
845
|
}
|