@quenty/nevermore-cli 4.38.0 → 4.40.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/CHANGELOG.md +12 -0
- package/dist/commands/batch-command/batch-deploy-command.js +1 -1
- package/dist/commands/batch-command/batch-deploy-command.js.map +1 -1
- package/dist/commands/deploy-command/index.d.ts.map +1 -1
- package/dist/commands/deploy-command/index.js +5 -2
- package/dist/commands/deploy-command/index.js.map +1 -1
- package/dist/commands/tools-command/strip-sourcemap-jest-command.d.ts +5 -1
- package/dist/commands/tools-command/strip-sourcemap-jest-command.d.ts.map +1 -1
- package/dist/commands/tools-command/strip-sourcemap-jest-command.js +40 -14
- package/dist/commands/tools-command/strip-sourcemap-jest-command.js.map +1 -1
- package/dist/utils/batch/changed-packages-utils.d.ts +8 -1
- package/dist/utils/batch/changed-packages-utils.d.ts.map +1 -1
- package/dist/utils/batch/changed-packages-utils.js +10 -2
- package/dist/utils/batch/changed-packages-utils.js.map +1 -1
- package/dist/utils/build/deploy-config.d.ts +14 -0
- package/dist/utils/build/deploy-config.d.ts.map +1 -1
- package/dist/utils/build/deploy-config.js +8 -0
- package/dist/utils/build/deploy-config.js.map +1 -1
- package/dist/utils/deploy/deploy-metadata.d.ts +7 -1
- package/dist/utils/deploy/deploy-metadata.d.ts.map +1 -1
- package/dist/utils/deploy/deploy-metadata.js +13 -1
- package/dist/utils/deploy/deploy-metadata.js.map +1 -1
- package/dist/utils/deploy/deploy-metadata.test.js +25 -0
- package/dist/utils/deploy/deploy-metadata.test.js.map +1 -1
- package/package.json +2 -2
- package/src/commands/batch-command/batch-deploy-command.ts +11 -7
- package/src/commands/deploy-command/index.ts +15 -7
- package/src/commands/tools-command/strip-sourcemap-jest-command.ts +46 -16
- package/src/utils/batch/changed-packages-utils.ts +18 -1
- package/src/utils/build/deploy-config.ts +22 -0
- package/src/utils/deploy/deploy-metadata.test.ts +28 -0
- package/src/utils/deploy/deploy-metadata.ts +17 -1
- package/tsconfig.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quenty/nevermore-cli",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.40.0",
|
|
4
4
|
"description": "CLI interface for Nevermore",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -57,5 +57,5 @@
|
|
|
57
57
|
"engines": {
|
|
58
58
|
"node": ">=16"
|
|
59
59
|
},
|
|
60
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "2c903e91ae2ea1702ae1ce94af20af83af81e4fe"
|
|
61
61
|
}
|
|
@@ -236,13 +236,17 @@ async function _runAsync(args: BatchDeployArgs): Promise<void> {
|
|
|
236
236
|
|
|
237
237
|
const injected = await injectDeployMetadataAsync(
|
|
238
238
|
builtPlace,
|
|
239
|
-
buildDeployMetadataAttributes(
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
239
|
+
buildDeployMetadataAttributes(
|
|
240
|
+
deployGitInfo,
|
|
241
|
+
{
|
|
242
|
+
target: targetName,
|
|
243
|
+
published: publish,
|
|
244
|
+
timestamp: deployTimestamp,
|
|
245
|
+
universeId: buildTarget.target.universeId,
|
|
246
|
+
placeId: buildTarget.target.placeId,
|
|
247
|
+
},
|
|
248
|
+
buildTarget.manifestPlaces
|
|
249
|
+
)
|
|
246
250
|
);
|
|
247
251
|
|
|
248
252
|
let version: number;
|
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
loadDeployConfigAsync,
|
|
35
35
|
resolveDeployConfigPath,
|
|
36
36
|
resolveDeployTargetPlaces,
|
|
37
|
+
toManifestPlaceInfo,
|
|
37
38
|
} from '../../utils/build/deploy-config.js';
|
|
38
39
|
import { handleInitAsync } from './deploy-init.js';
|
|
39
40
|
import {
|
|
@@ -256,6 +257,7 @@ export class DeployCommand<T> implements CommandModule<T, DeployArgs> {
|
|
|
256
257
|
|
|
257
258
|
const config = await loadDeployConfigAsync(resolveDeployConfigPath(cwd));
|
|
258
259
|
const places = resolveDeployTargetPlaces(config, targetName);
|
|
260
|
+
const manifestPlaces = places.map(toManifestPlaceInfo);
|
|
259
261
|
const isMultiPlace = places.length > 1;
|
|
260
262
|
|
|
261
263
|
if (isMultiPlace) {
|
|
@@ -279,6 +281,7 @@ export class DeployCommand<T> implements CommandModule<T, DeployArgs> {
|
|
|
279
281
|
packageName,
|
|
280
282
|
path: cwd,
|
|
281
283
|
target: place,
|
|
284
|
+
manifestPlaces,
|
|
282
285
|
};
|
|
283
286
|
})
|
|
284
287
|
: [
|
|
@@ -287,6 +290,7 @@ export class DeployCommand<T> implements CommandModule<T, DeployArgs> {
|
|
|
287
290
|
packageName,
|
|
288
291
|
path: cwd,
|
|
289
292
|
target: places[0]!,
|
|
293
|
+
manifestPlaces,
|
|
290
294
|
},
|
|
291
295
|
];
|
|
292
296
|
|
|
@@ -402,13 +406,17 @@ export class DeployCommand<T> implements CommandModule<T, DeployArgs> {
|
|
|
402
406
|
|
|
403
407
|
const injected = await injectDeployMetadataAsync(
|
|
404
408
|
builtPlace,
|
|
405
|
-
buildDeployMetadataAttributes(
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
409
|
+
buildDeployMetadataAttributes(
|
|
410
|
+
deployGitInfo,
|
|
411
|
+
{
|
|
412
|
+
target: targetName,
|
|
413
|
+
published: publish,
|
|
414
|
+
timestamp: deployTimestamp,
|
|
415
|
+
universeId: args.universeId ?? buildTarget.target.universeId,
|
|
416
|
+
placeId: args.placeId ?? buildTarget.target.placeId,
|
|
417
|
+
},
|
|
418
|
+
buildTarget.manifestPlaces
|
|
419
|
+
)
|
|
412
420
|
);
|
|
413
421
|
|
|
414
422
|
let uploadResult: Awaited<ReturnType<typeof uploadPlaceAsync>>;
|
|
@@ -10,13 +10,17 @@ interface StripSourcemapJestArgs extends NevermoreGlobalArgs {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* Temporary workaround:
|
|
13
|
+
* Temporary workaround: strip shadowing modules from Jest subtrees in sourcemap.json.
|
|
14
14
|
*
|
|
15
15
|
* luau-lsp's require-by-name resolution picks the first file matching a name
|
|
16
16
|
* in the sourcemap. Jest vendors internal copies of modules (Promise, string,
|
|
17
17
|
* symbol, etc.) that shadow Nevermore packages. The real loader handles this
|
|
18
18
|
* correctly, but luau-lsp doesn't yet.
|
|
19
19
|
*
|
|
20
|
+
* We drop only the Jest descendants whose name also exists elsewhere in the
|
|
21
|
+
* sourcemap (the shadowing copies), and keep the Jest-unique modules (Jest,
|
|
22
|
+
* JestGlobals, ...) so `require("Jest")` still resolves.
|
|
23
|
+
*
|
|
20
24
|
* Long-term fix: smarter require resolution in luau-lsp (plugins or fork).
|
|
21
25
|
*/
|
|
22
26
|
export const stripSourcemapJestCommand: CommandModule<
|
|
@@ -25,7 +29,7 @@ export const stripSourcemapJestCommand: CommandModule<
|
|
|
25
29
|
> = {
|
|
26
30
|
command: 'strip-sourcemap-jest',
|
|
27
31
|
describe:
|
|
28
|
-
|
|
32
|
+
"Strip modules from Jest subtrees in sourcemap.json that shadow names elsewhere, keeping require('Jest') resolvable",
|
|
29
33
|
builder: (yargs) => {
|
|
30
34
|
return yargs.option('sourcemap', {
|
|
31
35
|
describe: 'Path to sourcemap.json',
|
|
@@ -46,31 +50,57 @@ export const stripSourcemapJestCommand: CommandModule<
|
|
|
46
50
|
|
|
47
51
|
const sourcemap = JSON.parse(content) as SourcemapNode;
|
|
48
52
|
|
|
49
|
-
|
|
53
|
+
// Every module name that lives OUTSIDE any Jest subtree. Jest's vendored copies of these
|
|
54
|
+
// are what shadow the real Nevermore modules under luau-lsp's first-match resolution.
|
|
55
|
+
const externalNames = new Set<string>();
|
|
56
|
+
function collectExternalNames(
|
|
57
|
+
node: SourcemapNode,
|
|
58
|
+
insideJest: boolean
|
|
59
|
+
): void {
|
|
60
|
+
if (!node.children) return;
|
|
61
|
+
for (const child of node.children) {
|
|
62
|
+
const childInsideJest = insideJest || child.name === 'Jest';
|
|
63
|
+
if (!childInsideJest) {
|
|
64
|
+
externalNames.add(child.name);
|
|
65
|
+
}
|
|
66
|
+
collectExternalNames(child, childInsideJest);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
collectExternalNames(sourcemap, false);
|
|
50
70
|
|
|
51
|
-
|
|
71
|
+
// Within each Jest subtree, drop any node whose name also exists elsewhere (the shadowing
|
|
72
|
+
// copies), keeping the Jest-unique modules so require("Jest") still resolves. Also drop a
|
|
73
|
+
// Jest node nested inside another Jest: the jest-lua package folder ("Jest", whose
|
|
74
|
+
// init.lua exports `.Globals`) contains a leaf "Jest" submodule that lacks `.Globals`, and
|
|
75
|
+
// the duplicate name makes require("Jest") resolve to the wrong one for some packages.
|
|
76
|
+
let stripped = 0;
|
|
77
|
+
function stripShadowingNodes(
|
|
78
|
+
node: SourcemapNode,
|
|
79
|
+
insideJest: boolean
|
|
80
|
+
): void {
|
|
52
81
|
if (!node.children) return;
|
|
53
82
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
83
|
+
if (insideJest) {
|
|
84
|
+
node.children = node.children.filter((child) => {
|
|
85
|
+
if (externalNames.has(child.name) || child.name === 'Jest') {
|
|
86
|
+
stripped++;
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
return true;
|
|
90
|
+
});
|
|
91
|
+
}
|
|
61
92
|
|
|
62
93
|
for (const child of node.children) {
|
|
63
|
-
|
|
94
|
+
stripShadowingNodes(child, insideJest || child.name === 'Jest');
|
|
64
95
|
}
|
|
65
96
|
}
|
|
66
|
-
|
|
67
|
-
removeJestNodes(sourcemap);
|
|
97
|
+
stripShadowingNodes(sourcemap, false);
|
|
68
98
|
|
|
69
99
|
fs.writeFileSync(sourcemapPath, JSON.stringify(sourcemap));
|
|
70
100
|
|
|
71
|
-
if (
|
|
101
|
+
if (stripped > 0) {
|
|
72
102
|
OutputHelper.info(
|
|
73
|
-
`
|
|
103
|
+
`Stripped ${stripped} shadowing node(s) from Jest subtrees in ${sourcemapPath}`
|
|
74
104
|
);
|
|
75
105
|
}
|
|
76
106
|
},
|
|
@@ -3,9 +3,11 @@ import { execa } from 'execa';
|
|
|
3
3
|
import { OutputHelper } from '@quenty/cli-output-helpers';
|
|
4
4
|
import {
|
|
5
5
|
DeployTarget,
|
|
6
|
+
ManifestPlaceInfo,
|
|
6
7
|
loadDeployConfigAsync,
|
|
7
8
|
resolveDeployConfigPath,
|
|
8
9
|
resolveDeployTargetPlaces,
|
|
10
|
+
toManifestPlaceInfo,
|
|
9
11
|
} from '../build/deploy-config.js';
|
|
10
12
|
|
|
11
13
|
export interface TargetPackage {
|
|
@@ -33,6 +35,13 @@ export interface BatchTarget {
|
|
|
33
35
|
path: string;
|
|
34
36
|
/** The single resolved place this BatchTarget represents. */
|
|
35
37
|
target: DeployTarget;
|
|
38
|
+
/**
|
|
39
|
+
* Every place of this package's target (including `target` itself), stamped
|
|
40
|
+
* into the runtime manifest so a deployed place can resolve its siblings' IDs.
|
|
41
|
+
* Same for every BatchTarget fanned out from one package. Only the deploy
|
|
42
|
+
* paths populate this; test/script BatchTargets omit it.
|
|
43
|
+
*/
|
|
44
|
+
manifestPlaces?: ManifestPlaceInfo[];
|
|
36
45
|
}
|
|
37
46
|
|
|
38
47
|
/**
|
|
@@ -119,19 +128,27 @@ export function flattenToBatchTargets(
|
|
|
119
128
|
): BatchTarget[] {
|
|
120
129
|
const result: BatchTarget[] = [];
|
|
121
130
|
for (const pkg of packages) {
|
|
131
|
+
const manifestPlaces = pkg.activeTargets.map(toManifestPlaceInfo);
|
|
122
132
|
if (pkg.activeTargets.length === 1) {
|
|
123
133
|
result.push({
|
|
124
134
|
name: pkg.name,
|
|
125
135
|
packageName: pkg.name,
|
|
126
136
|
path: pkg.path,
|
|
127
137
|
target: pkg.activeTargets[0]!,
|
|
138
|
+
manifestPlaces,
|
|
128
139
|
});
|
|
129
140
|
continue;
|
|
130
141
|
}
|
|
131
142
|
for (const target of pkg.activeTargets) {
|
|
132
143
|
const suffix = target.name;
|
|
133
144
|
const name = suffix ? `${pkg.name} - ${suffix}` : pkg.name;
|
|
134
|
-
result.push({
|
|
145
|
+
result.push({
|
|
146
|
+
name,
|
|
147
|
+
packageName: pkg.name,
|
|
148
|
+
path: pkg.path,
|
|
149
|
+
target,
|
|
150
|
+
manifestPlaces,
|
|
151
|
+
});
|
|
135
152
|
}
|
|
136
153
|
}
|
|
137
154
|
return result;
|
|
@@ -36,6 +36,28 @@ export interface DeployConfig {
|
|
|
36
36
|
targets: Record<string, DeployTargetConfig>;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* The subset of a resolved place that gets baked into the runtime manifest
|
|
41
|
+
* (see the nevermore-cli-manifest package). A deployed place carries the whole
|
|
42
|
+
* target's place table so it can resolve its siblings' IDs at runtime — e.g. a
|
|
43
|
+
* chapter place teleporting to another chapter without hard-coding place IDs.
|
|
44
|
+
*/
|
|
45
|
+
export interface ManifestPlaceInfo {
|
|
46
|
+
/** Place name from a multi-place target (e.g. "chapter0"); absent for single-place targets. */
|
|
47
|
+
name?: string;
|
|
48
|
+
placeId: number;
|
|
49
|
+
universeId: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Project a resolved deploy place down to the fields stamped into the manifest. */
|
|
53
|
+
export function toManifestPlaceInfo(place: DeployTarget): ManifestPlaceInfo {
|
|
54
|
+
return {
|
|
55
|
+
name: place.name,
|
|
56
|
+
placeId: place.placeId,
|
|
57
|
+
universeId: place.universeId,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
39
61
|
function _isMultiPlace(
|
|
40
62
|
target: DeployTargetConfig
|
|
41
63
|
): target is MultiPlaceTargetConfig {
|
|
@@ -84,6 +84,34 @@ describe('buildDeployMetadataAttributes', () => {
|
|
|
84
84
|
);
|
|
85
85
|
expect(attributes.Published).toBe(false);
|
|
86
86
|
});
|
|
87
|
+
|
|
88
|
+
it('omits Places when no place table is given', () => {
|
|
89
|
+
const attributes = buildDeployMetadataAttributes({}, PLACE);
|
|
90
|
+
expect('Places' in attributes).toBe(false);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('stamps Places as a JSON string preserving large IDs exactly', () => {
|
|
94
|
+
const attributes = buildDeployMetadataAttributes({}, PLACE, [
|
|
95
|
+
{ name: 'chapter0', placeId: 97235312452456, universeId: 10192566764 },
|
|
96
|
+
{ name: 'chapter1', placeId: 87639818897831, universeId: 10192566764 },
|
|
97
|
+
]);
|
|
98
|
+
expect(typeof attributes.Places).toBe('string');
|
|
99
|
+
const parsed = JSON.parse(attributes.Places as string);
|
|
100
|
+
// Large place IDs must survive exactly (JSON text, not a float32 attribute).
|
|
101
|
+
expect(parsed).toEqual([
|
|
102
|
+
{ name: 'chapter0', placeId: 97235312452456, universeId: 10192566764 },
|
|
103
|
+
{ name: 'chapter1', placeId: 87639818897831, universeId: 10192566764 },
|
|
104
|
+
]);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('omits the name key for a single-place (nameless) target', () => {
|
|
108
|
+
const attributes = buildDeployMetadataAttributes({}, PLACE, [
|
|
109
|
+
{ placeId: 123, universeId: 456 },
|
|
110
|
+
]);
|
|
111
|
+
const parsed = JSON.parse(attributes.Places as string);
|
|
112
|
+
expect(parsed).toEqual([{ placeId: 123, universeId: 456 }]);
|
|
113
|
+
expect('name' in parsed[0]).toBe(false);
|
|
114
|
+
});
|
|
87
115
|
});
|
|
88
116
|
|
|
89
117
|
describe('packageUsesManifestAsync', () => {
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from '@quenty/nevermore-template-helpers';
|
|
8
8
|
import { OutputHelper } from '@quenty/cli-output-helpers';
|
|
9
9
|
import { type BuiltPlace } from '../build/build.js';
|
|
10
|
+
import { type ManifestPlaceInfo } from '../build/deploy-config.js';
|
|
10
11
|
|
|
11
12
|
/** npm name of the package that ships the manifest module. */
|
|
12
13
|
export const MANIFEST_PACKAGE_NAME = '@quenty/nevermoreclimanifest';
|
|
@@ -85,10 +86,16 @@ export interface DeployPlaceInfo {
|
|
|
85
86
|
* round-trip through Lune serialization as float32, which silently corrupts IDs
|
|
86
87
|
* above 2^24 (e.g. 123456789 -> 123456792). Strings are exact; the Luau reader
|
|
87
88
|
* converts them back with `tonumber`.
|
|
89
|
+
*
|
|
90
|
+
* When `places` is given (the whole target's place table), it is stamped as a
|
|
91
|
+
* `Places` JSON-string attribute. The IDs inside stay numeric — a JSON string
|
|
92
|
+
* is exact text, and the Luau reader decodes it to 64-bit numbers, so the
|
|
93
|
+
* float32 hazard that forces the top-level IDs to be stringified doesn't apply.
|
|
88
94
|
*/
|
|
89
95
|
export function buildDeployMetadataAttributes(
|
|
90
96
|
git: GitDeployInfo,
|
|
91
|
-
place: DeployPlaceInfo
|
|
97
|
+
place: DeployPlaceInfo,
|
|
98
|
+
places?: readonly ManifestPlaceInfo[]
|
|
92
99
|
): DeployMetadataAttributes {
|
|
93
100
|
const attributes: DeployMetadataAttributes = {
|
|
94
101
|
Deployed: true,
|
|
@@ -101,6 +108,15 @@ export function buildDeployMetadataAttributes(
|
|
|
101
108
|
if (git.commit) attributes.Commit = git.commit;
|
|
102
109
|
if (git.version) attributes.Version = git.version;
|
|
103
110
|
if (git.branch) attributes.Branch = git.branch;
|
|
111
|
+
if (places && places.length > 0) {
|
|
112
|
+
attributes.Places = JSON.stringify(
|
|
113
|
+
places.map((p) => ({
|
|
114
|
+
name: p.name,
|
|
115
|
+
placeId: p.placeId,
|
|
116
|
+
universeId: p.universeId,
|
|
117
|
+
}))
|
|
118
|
+
);
|
|
119
|
+
}
|
|
104
120
|
return attributes;
|
|
105
121
|
}
|
|
106
122
|
|