@flighthq/vite-plugin-manifest 0.5.1-edge.1685.dc0e23a
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.md +9 -0
- package/README.md +17 -0
- package/dist/contentAnalyzers.d.ts +33 -0
- package/dist/contentAnalyzers.d.ts.map +1 -0
- package/dist/contentAnalyzers.js +37 -0
- package/dist/contentAnalyzers.js.map +1 -0
- package/dist/contract.d.ts +5 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +5 -0
- package/dist/contract.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/manifestModuleSource.d.ts +41 -0
- package/dist/manifestModuleSource.d.ts.map +1 -0
- package/dist/manifestModuleSource.js +131 -0
- package/dist/manifestModuleSource.js.map +1 -0
- package/dist/manifestPlugin.d.ts +53 -0
- package/dist/manifestPlugin.d.ts.map +1 -0
- package/dist/manifestPlugin.js +112 -0
- package/dist/manifestPlugin.js.map +1 -0
- package/dist/requirementOptionFields.d.ts +32 -0
- package/dist/requirementOptionFields.d.ts.map +1 -0
- package/dist/requirementOptionFields.js +76 -0
- package/dist/requirementOptionFields.js.map +1 -0
- package/package.json +66 -0
- package/src/contentAnalyzers.test.ts +106 -0
- package/src/manifestComposition.test.ts +33 -0
- package/src/manifestModuleSource.test.ts +179 -0
- package/src/manifestPlugin.test.ts +212 -0
- package/src/manifestTreeShaking.test.ts +172 -0
- package/src/manifestTypedConsumer.test.ts +114 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2013-2026 Joshua Granick and other contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# @flighthq/vite-plugin-manifest
|
|
2
|
+
|
|
3
|
+
Vite plugin serving generated Flight registration source from analyzed content
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install @flighthq/vite-plugin-manifest
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Import the supported application-facing API from `@flighthq/vite-plugin-manifest`. The `@flighthq/vite-plugin-manifest/contract` export is the wider package-to-package contract used to compose Flight itself.
|
|
12
|
+
|
|
13
|
+
This package is part of the locked-version Flight SDK graph. Applications may instead install and import `@flighthq/sdk` when package-level tree shaking is sufficient.
|
|
14
|
+
|
|
15
|
+
- [Flight project](https://github.com/flighthq/flight)
|
|
16
|
+
- [Source for @flighthq/vite-plugin-manifest@0.5.1-edge.1685.dc0e23a](https://github.com/flighthq/flight/tree/dc0e23a17453ec75c20ccbf07058a57b5ef10c4c/packages/vite-plugin-manifest)
|
|
17
|
+
- [License](https://github.com/flighthq/flight/blob/dc0e23a17453ec75c20ccbf07058a57b5ef10c4c/LICENSE.md)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { HostDecompressDeflateCapability, HostDecompressLzmaCapability, RequirementSet } from '@flighthq/types/contract';
|
|
2
|
+
/** The decompressors a build supplies so compressed content can be read. */
|
|
3
|
+
export interface ContentDecompressors {
|
|
4
|
+
readonly deflate: Readonly<HostDecompressDeflateCapability> | null;
|
|
5
|
+
readonly lzma: Readonly<HostDecompressLzmaCapability> | null;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* How one format is read: whether the bytes are readable at all, and what they require.
|
|
9
|
+
*
|
|
10
|
+
* `isReadable` exists because the two questions have DIFFERENT answers and only one of them is
|
|
11
|
+
* visible in a `RequirementSet`. An analyzer returns an empty set both for a file that genuinely
|
|
12
|
+
* requires nothing AND for a file it could not decode — a `CWS` SWF or a deflate AWD when no
|
|
13
|
+
* decompressor was supplied. Without a separate readability probe the plugin cannot tell those apart,
|
|
14
|
+
* and the second one silently produces a bundle missing every handler the content needed. The header
|
|
15
|
+
* parsers answer it for a bounded prefix, which is exactly what they are for.
|
|
16
|
+
*/
|
|
17
|
+
export interface ContentAnalyzer {
|
|
18
|
+
readonly analyze: (source: Uint8Array, decompressors: Readonly<ContentDecompressors>) => RequirementSet;
|
|
19
|
+
readonly isReadable: (source: Uint8Array, decompressors: Readonly<ContentDecompressors>) => boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The formats Flight analyzes, keyed by lowercase file extension.
|
|
23
|
+
*
|
|
24
|
+
* These call the format packages' own exports directly. Duplicating their logic here would let a
|
|
25
|
+
* build's idea of what a file needs drift from what the importer actually reads — the two must be the
|
|
26
|
+
* same walk or the manifest is a guess.
|
|
27
|
+
*
|
|
28
|
+
* Away3D writes `.awd`; `.awd2` is accepted too because the format is AWD2 and projects name the file
|
|
29
|
+
* either way. Both resolve to the same analyzer, so the choice of suffix never changes what a build
|
|
30
|
+
* reads.
|
|
31
|
+
*/
|
|
32
|
+
export declare const DEFAULT_CONTENT_ANALYZERS: Readonly<Record<string, ContentAnalyzer>>;
|
|
33
|
+
//# sourceMappingURL=contentAnalyzers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contentAnalyzers.d.ts","sourceRoot":"","sources":["../src/contentAnalyzers.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,+BAA+B,EAC/B,4BAA4B,EAC5B,cAAc,EACf,MAAM,0BAA0B,CAAC;AAElC,4EAA4E;AAC5E,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,+BAA+B,CAAC,GAAG,IAAI,CAAC;IACnE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,4BAA4B,CAAC,GAAG,IAAI,CAAC;CAC9D;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,QAAQ,CAAC,oBAAoB,CAAC,KAAK,cAAc,CAAC;IACxG,QAAQ,CAAC,UAAU,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,QAAQ,CAAC,oBAAoB,CAAC,KAAK,OAAO,CAAC;CACrG;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAO9E,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { collectAwd2BlockCounts, parseAwd2Requirements } from '@flighthq/scene3d-formats/contract';
|
|
2
|
+
import { parseSwfHeader, parseSwfRequirements } from '@flighthq/swf/contract';
|
|
3
|
+
/**
|
|
4
|
+
* The formats Flight analyzes, keyed by lowercase file extension.
|
|
5
|
+
*
|
|
6
|
+
* These call the format packages' own exports directly. Duplicating their logic here would let a
|
|
7
|
+
* build's idea of what a file needs drift from what the importer actually reads — the two must be the
|
|
8
|
+
* same walk or the manifest is a guess.
|
|
9
|
+
*
|
|
10
|
+
* Away3D writes `.awd`; `.awd2` is accepted too because the format is AWD2 and projects name the file
|
|
11
|
+
* either way. Both resolve to the same analyzer, so the choice of suffix never changes what a build
|
|
12
|
+
* reads.
|
|
13
|
+
*/
|
|
14
|
+
export const DEFAULT_CONTENT_ANALYZERS = Object.freeze({
|
|
15
|
+
'.awd': AWD2_ANALYZER(),
|
|
16
|
+
'.awd2': AWD2_ANALYZER(),
|
|
17
|
+
'.swf': {
|
|
18
|
+
analyze: (source, { deflate, lzma }) => parseSwfRequirements(source, deflate, lzma),
|
|
19
|
+
isReadable: (source, { deflate, lzma }) => parseSwfHeader(source, deflate, lzma) !== null,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
// The two formats need DIFFERENT readability probes, because they compress different things.
|
|
23
|
+
//
|
|
24
|
+
// SWF compresses the whole body behind the 8-byte signature, so `parseSwfHeader` already fails on a
|
|
25
|
+
// `CWS` file with no deflate capability — a bounded check that answers the question exactly.
|
|
26
|
+
//
|
|
27
|
+
// AWD2 compresses only the body: its 12-byte header is plain bytes and `parseAwd2Header` succeeds even
|
|
28
|
+
// when nothing can read what follows. Probing the header there would report a deflate AWD as readable
|
|
29
|
+
// and reintroduce the silent-empty hole this exists to close, so AWD2 probes the block census, which
|
|
30
|
+
// is the step that actually rehydrates.
|
|
31
|
+
function AWD2_ANALYZER() {
|
|
32
|
+
return {
|
|
33
|
+
analyze: (source, { deflate, lzma }) => parseAwd2Requirements(source, deflate, lzma),
|
|
34
|
+
isReadable: (source, { deflate, lzma }) => collectAwd2BlockCounts(source, deflate, lzma) !== null,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=contentAnalyzers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contentAnalyzers.js","sourceRoot":"","sources":["../src/contentAnalyzers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AACnG,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AA4B9E;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAA8C,MAAM,CAAC,MAAM,CAAC;IAChG,MAAM,EAAE,aAAa,EAAE;IACvB,OAAO,EAAE,aAAa,EAAE;IACxB,MAAM,EAAE;QACN,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,oBAAoB,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;QACnF,UAAU,EAAE,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,IAAI;KAC1F;CACF,CAAC,CAAC;AAEH,6FAA6F;AAC7F,EAAE;AACF,oGAAoG;AACpG,6FAA6F;AAC7F,EAAE;AACF,uGAAuG;AACvG,sGAAsG;AACtG,qGAAqG;AACrG,wCAAwC;AACxC,SAAS,aAAa;IACpB,OAAO;QACL,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,qBAAqB,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;QACpF,UAAU,EAAE,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,sBAAsB,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,IAAI;KAClG,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC"}
|
package/dist/contract.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { RequirementCatalogEntry } from '@flighthq/types/contract';
|
|
2
|
+
/** The backends a manifest module always exports a fragment for, and the export name each one uses. */
|
|
3
|
+
export declare const MANIFEST_BACKEND_EXPORTS: Readonly<Record<string, string>>;
|
|
4
|
+
/**
|
|
5
|
+
* The catalog backend whose rows become `parserOptions` rather than a render-state fragment.
|
|
6
|
+
*
|
|
7
|
+
* A row's DESTINATION is its backend, not its facet: the same `document.format` requirement resolves
|
|
8
|
+
* to a tag handler for the parser and to a node renderer for `canvas`, and only the catalog author
|
|
9
|
+
* knows which row is which. Routing by facet alone would send both to the same place.
|
|
10
|
+
*/
|
|
11
|
+
export declare const MANIFEST_PARSER_BACKEND = "parser";
|
|
12
|
+
/** One resolved row: the catalog entry, and the requirement kind it satisfies. */
|
|
13
|
+
export interface ManifestModuleEntry {
|
|
14
|
+
readonly entry: Readonly<RequirementCatalogEntry>;
|
|
15
|
+
readonly kind: string;
|
|
16
|
+
}
|
|
17
|
+
/** What the emitter produced, and every row it could not place. */
|
|
18
|
+
export interface ManifestModuleResult {
|
|
19
|
+
/** Rows dropped with the reason, so a caller reports them instead of losing them silently. */
|
|
20
|
+
readonly problems: readonly string[];
|
|
21
|
+
readonly source: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Emits the per-file manifest module for one content file.
|
|
25
|
+
*
|
|
26
|
+
* Flat named exports — `canvasOptions`, `glOptions`, `wgpuOptions`, `domOptions`, `parserOptions` — so
|
|
27
|
+
* an application writes `import { glOptions } from './asset.swf?manifest'` and spreads it straight
|
|
28
|
+
* into `createGlRenderState`. Flat flat named bindings are what make the module shakable: a bundler
|
|
29
|
+
* that sees only `glOptions` imported can drop every other fragment AND the implementation modules
|
|
30
|
+
* only they referenced, which it could not do if the fragments hung off one default object.
|
|
31
|
+
*
|
|
32
|
+
* Every referenced symbol gets a precise named import from the module the catalog row names. There is
|
|
33
|
+
* no barrel import and no dynamic lookup, so the import graph states exactly which implementations
|
|
34
|
+
* this one file needs.
|
|
35
|
+
*
|
|
36
|
+
* Deterministic: imports are grouped by module with modules and symbols sorted, map entries are
|
|
37
|
+
* emitted in sorted kind order, and array entries keep catalog order. The same inputs always produce
|
|
38
|
+
* byte-identical source.
|
|
39
|
+
*/
|
|
40
|
+
export declare function generateManifestModuleSource(rows: readonly ManifestModuleEntry[], extension: string): ManifestModuleResult;
|
|
41
|
+
//# sourceMappingURL=manifestModuleSource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifestModuleSource.d.ts","sourceRoot":"","sources":["../src/manifestModuleSource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAIxE,uGAAuG;AACvG,eAAO,MAAM,wBAAwB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAKpE,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,WAAW,CAAC;AAEhD,kFAAkF;AAClF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,uBAAuB,CAAC,CAAC;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,mEAAmE;AACnE,MAAM,WAAW,oBAAoB;IACnC,8FAA8F;IAC9F,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,SAAS,mBAAmB,EAAE,EACpC,SAAS,EAAE,MAAM,GAChB,oBAAoB,CAuDtB"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { BACKEND_OPTION_FIELDS, PARSER_HANDLER_FIELDS, REQUIREMENT_OPTION_FIELDS } from './requirementOptionFields';
|
|
2
|
+
/** The backends a manifest module always exports a fragment for, and the export name each one uses. */
|
|
3
|
+
export const MANIFEST_BACKEND_EXPORTS = Object.freeze({
|
|
4
|
+
canvas: 'canvasOptions',
|
|
5
|
+
dom: 'domOptions',
|
|
6
|
+
gl: 'glOptions',
|
|
7
|
+
wgpu: 'wgpuOptions',
|
|
8
|
+
});
|
|
9
|
+
/**
|
|
10
|
+
* The catalog backend whose rows become `parserOptions` rather than a render-state fragment.
|
|
11
|
+
*
|
|
12
|
+
* A row's DESTINATION is its backend, not its facet: the same `document.format` requirement resolves
|
|
13
|
+
* to a tag handler for the parser and to a node renderer for `canvas`, and only the catalog author
|
|
14
|
+
* knows which row is which. Routing by facet alone would send both to the same place.
|
|
15
|
+
*/
|
|
16
|
+
export const MANIFEST_PARSER_BACKEND = 'parser';
|
|
17
|
+
/**
|
|
18
|
+
* Emits the per-file manifest module for one content file.
|
|
19
|
+
*
|
|
20
|
+
* Flat named exports — `canvasOptions`, `glOptions`, `wgpuOptions`, `domOptions`, `parserOptions` — so
|
|
21
|
+
* an application writes `import { glOptions } from './asset.swf?manifest'` and spreads it straight
|
|
22
|
+
* into `createGlRenderState`. Flat flat named bindings are what make the module shakable: a bundler
|
|
23
|
+
* that sees only `glOptions` imported can drop every other fragment AND the implementation modules
|
|
24
|
+
* only they referenced, which it could not do if the fragments hung off one default object.
|
|
25
|
+
*
|
|
26
|
+
* Every referenced symbol gets a precise named import from the module the catalog row names. There is
|
|
27
|
+
* no barrel import and no dynamic lookup, so the import graph states exactly which implementations
|
|
28
|
+
* this one file needs.
|
|
29
|
+
*
|
|
30
|
+
* Deterministic: imports are grouped by module with modules and symbols sorted, map entries are
|
|
31
|
+
* emitted in sorted kind order, and array entries keep catalog order. The same inputs always produce
|
|
32
|
+
* byte-identical source.
|
|
33
|
+
*/
|
|
34
|
+
export function generateManifestModuleSource(rows, extension) {
|
|
35
|
+
const importsByModule = new Map();
|
|
36
|
+
const byBackend = new Map();
|
|
37
|
+
const parserRows = [];
|
|
38
|
+
const problems = [];
|
|
39
|
+
for (const row of rows) {
|
|
40
|
+
if (row.entry.backend === MANIFEST_PARSER_BACKEND) {
|
|
41
|
+
addImport(importsByModule, row.entry.implementationImport, row.entry.implementationSymbol);
|
|
42
|
+
parserRows.push(row);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
// Two ways a render-backend row cannot be placed, and NEITHER is a silent skip. A facet with no
|
|
46
|
+
// render-state field at all (compression, physics, resource mime type) has nowhere to go; and a
|
|
47
|
+
// field this particular backend does not declare — blendRealizations on WGPU, say — would emit a
|
|
48
|
+
// fragment that spreads into nothing. Both are reported so the build can see what it lost.
|
|
49
|
+
const field = REQUIREMENT_OPTION_FIELDS[row.entry.facet];
|
|
50
|
+
if (field === undefined) {
|
|
51
|
+
problems.push(`facet ${row.entry.facet} has no render-state options field: dropped ${row.kind} for ${row.entry.backend}`);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const accepted = BACKEND_OPTION_FIELDS[row.entry.backend];
|
|
55
|
+
if (accepted === undefined) {
|
|
56
|
+
problems.push(`unknown backend ${row.entry.backend}: dropped ${row.entry.facet} ${row.kind}`);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (!accepted.has(field)) {
|
|
60
|
+
problems.push(`backend ${row.entry.backend} has no ${field} field: dropped ${row.entry.facet} ${row.kind}`);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
addImport(importsByModule, row.entry.implementationImport, row.entry.implementationSymbol);
|
|
64
|
+
let list = byBackend.get(row.entry.backend);
|
|
65
|
+
if (list === undefined) {
|
|
66
|
+
list = [];
|
|
67
|
+
byBackend.set(row.entry.backend, list);
|
|
68
|
+
}
|
|
69
|
+
list.push(row);
|
|
70
|
+
}
|
|
71
|
+
const lines = [
|
|
72
|
+
'// GENERATED by @flighthq/vite-plugin-manifest. Do not edit.',
|
|
73
|
+
`// Requirements of one ${extension} file, resolved for every backend in the catalog.`,
|
|
74
|
+
];
|
|
75
|
+
const importLines = [...importsByModule.keys()]
|
|
76
|
+
.sort()
|
|
77
|
+
.map((module) => `import { ${[...importsByModule.get(module)].sort().join(', ')} } from '${module}';`);
|
|
78
|
+
if (importLines.length > 0)
|
|
79
|
+
lines.push('', ...importLines);
|
|
80
|
+
for (const backend of Object.keys(MANIFEST_BACKEND_EXPORTS).sort()) {
|
|
81
|
+
lines.push('', ...backendFragment(MANIFEST_BACKEND_EXPORTS[backend], byBackend.get(backend) ?? []));
|
|
82
|
+
}
|
|
83
|
+
lines.push('', ...parserFragment(parserRows, PARSER_HANDLER_FIELDS[extension] ?? 'handlers'));
|
|
84
|
+
return { problems, source: `${lines.join('\n')}\n` };
|
|
85
|
+
}
|
|
86
|
+
function addImport(importsByModule, module, symbol) {
|
|
87
|
+
let symbols = importsByModule.get(module);
|
|
88
|
+
if (symbols === undefined) {
|
|
89
|
+
symbols = new Set();
|
|
90
|
+
importsByModule.set(module, symbols);
|
|
91
|
+
}
|
|
92
|
+
symbols.add(symbol);
|
|
93
|
+
}
|
|
94
|
+
function backendFragment(exportName, rows) {
|
|
95
|
+
const byField = new Map();
|
|
96
|
+
for (const row of rows) {
|
|
97
|
+
const field = REQUIREMENT_OPTION_FIELDS[row.entry.facet];
|
|
98
|
+
let list = byField.get(field);
|
|
99
|
+
if (list === undefined) {
|
|
100
|
+
list = [];
|
|
101
|
+
byField.set(field, list);
|
|
102
|
+
}
|
|
103
|
+
list.push(row);
|
|
104
|
+
}
|
|
105
|
+
// An empty fragment is still exported. A backend this content needs nothing for must spread to
|
|
106
|
+
// nothing rather than fail to import, so an application can spread every fragment unconditionally.
|
|
107
|
+
if (byField.size === 0)
|
|
108
|
+
return [`export const ${exportName} = {};`];
|
|
109
|
+
const fields = [...byField.keys()].sort().map((field) => {
|
|
110
|
+
const entries = [...byField.get(field)]
|
|
111
|
+
.sort((a, b) => a.kind.localeCompare(b.kind))
|
|
112
|
+
.map((row) => ` ['${row.kind}', ${row.entry.implementationSymbol}],`);
|
|
113
|
+
return ` ${field}: new Map([\n${entries.join('\n')}\n ]),`;
|
|
114
|
+
});
|
|
115
|
+
return [`export const ${exportName} = {`, ...fields, '};'];
|
|
116
|
+
}
|
|
117
|
+
function parserFragment(rows, field) {
|
|
118
|
+
if (rows.length === 0)
|
|
119
|
+
return ['export const parserOptions = {};'];
|
|
120
|
+
// Emitted in the order the rows arrive, which is NOT catalog order: `createRequirementSet`
|
|
121
|
+
// canonicalizes by facet then key, and the codegen plan walks that, so handlers come out sorted by
|
|
122
|
+
// kind. Downstream measurement confirmed this is safe — AWD2 build phases run in caller array order
|
|
123
|
+
// but read state populated by the Map-driven walk, which is order-independent, and PolarBear.awd and
|
|
124
|
+
// tictac.awd produce byte-identical documents under sorted versus preset order. Recorded because the
|
|
125
|
+
// ordering here is a CONSEQUENCE of canonicalization rather than a guarantee this function makes: a
|
|
126
|
+
// format whose handlers genuinely contend for the same key would need its own ordering, and this
|
|
127
|
+
// comment is the warning that it would not get one for free.
|
|
128
|
+
const handlers = rows.map((row) => ` ${row.entry.implementationSymbol},`);
|
|
129
|
+
return [`export const parserOptions = {`, ` ${field}: [`, ...handlers, ' ],', '};'];
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=manifestModuleSource.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifestModuleSource.js","sourceRoot":"","sources":["../src/manifestModuleSource.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { HostDecompressDeflateCapability, HostDecompressLzmaCapability, NonEntityCreateResult, RequirementCatalog } from '@flighthq/types/contract';
|
|
2
|
+
/** The import suffix that asks this plugin for a file's manifest instead of the file itself. */
|
|
3
|
+
export declare const MANIFEST_QUERY_SUFFIX = "?manifest";
|
|
4
|
+
/** What `createManifestPlugin` accepts. Every input is explicit; nothing is discovered by convention. */
|
|
5
|
+
export interface ManifestPluginOptions {
|
|
6
|
+
/** The catalog mapping a requirement to the implementation that satisfies it, for every backend. */
|
|
7
|
+
readonly catalog: Readonly<RequirementCatalog>;
|
|
8
|
+
/**
|
|
9
|
+
* Inflates a `CWS` SWF body or a deflate-compressed AWD2 body. Without it, compressed content cannot
|
|
10
|
+
* be read at all — and an unreadable file is REPORTED rather than analyzed to an empty manifest,
|
|
11
|
+
* because a silently empty manifest is a bundle missing every handler the content needed.
|
|
12
|
+
*/
|
|
13
|
+
readonly deflate?: Readonly<HostDecompressDeflateCapability> | null;
|
|
14
|
+
/** Decodes a `ZWS` SWF body or an LZMA-compressed AWD2 body. Absent behaves as for `deflate`. */
|
|
15
|
+
readonly lzma?: Readonly<HostDecompressLzmaCapability> | null;
|
|
16
|
+
/**
|
|
17
|
+
* Called for every unsupported format, unreadable file, analyzer failure and unresolved requirement.
|
|
18
|
+
* Defaults to a warning on the `flight-manifest` channel through `@flighthq/log`. A requirement the
|
|
19
|
+
* build cannot place is reported rather than skipped: silently dropping one is how a bundle ends up
|
|
20
|
+
* missing an implementation it needed.
|
|
21
|
+
*/
|
|
22
|
+
readonly onDiagnostic?: (message: string) => void;
|
|
23
|
+
}
|
|
24
|
+
/** The subset of Vite's plugin surface this factory produces. Structural, so Vite is a peer, not a dep. */
|
|
25
|
+
export interface ManifestPlugin {
|
|
26
|
+
/**
|
|
27
|
+
* Runs in Vite's `pre` stage. Without it Vite's own asset handling claims `./asset.swf?manifest`
|
|
28
|
+
* first and hands rollup the binary to parse as JavaScript — the plugin must see the id before the
|
|
29
|
+
* default resolver treats the path as a file to serve.
|
|
30
|
+
*/
|
|
31
|
+
readonly enforce: 'pre';
|
|
32
|
+
readonly handleHotUpdate: (context: {
|
|
33
|
+
readonly file: string;
|
|
34
|
+
}) => void;
|
|
35
|
+
readonly load: (id: string) => Promise<string | null>;
|
|
36
|
+
readonly name: string;
|
|
37
|
+
readonly resolveId: (id: string, importer?: string) => string | null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Creates the plugin. Explicit factory, no default export and no module-level state: importing this
|
|
41
|
+
* package configures nothing.
|
|
42
|
+
*
|
|
43
|
+
* It intercepts a per-file import — `import { glOptions } from './asset.swf?manifest'` — reads THAT
|
|
44
|
+
* file, picks the analyzer by extension, resolves the requirements against the catalog for every
|
|
45
|
+
* backend, and serves a module of flat per-backend fragments. Per-file rather than project-wide
|
|
46
|
+
* because the import graph is then the truth: a document nothing imports contributes nothing, and a
|
|
47
|
+
* bundler can see which implementations each document actually pulled in.
|
|
48
|
+
*
|
|
49
|
+
* The pipeline is the SDK's own — `parse*Requirements` to read content, the catalog to resolve. This
|
|
50
|
+
* package contributes the Vite lifecycle and the module text, nothing else.
|
|
51
|
+
*/
|
|
52
|
+
export declare function createManifestPlugin(options: Readonly<ManifestPluginOptions>): NonEntityCreateResult<ManifestPlugin, 'descriptor'>;
|
|
53
|
+
//# sourceMappingURL=manifestPlugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifestPlugin.d.ts","sourceRoot":"","sources":["../src/manifestPlugin.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,+BAA+B,EAC/B,4BAA4B,EAC5B,qBAAqB,EAErB,kBAAkB,EACnB,MAAM,0BAA0B,CAAC;AAUlC,gGAAgG;AAChG,eAAO,MAAM,qBAAqB,cAAc,CAAC;AAEjD,yGAAyG;AACzG,MAAM,WAAW,qBAAqB;IACpC,oGAAoG;IACpG,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,kBAAkB,CAAC,CAAC;IAC/C;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,+BAA+B,CAAC,GAAG,IAAI,CAAC;IACpE,iGAAiG;IACjG,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,4BAA4B,CAAC,GAAG,IAAI,CAAC;IAC9D;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACnD;AAED,2GAA2G;AAC3G,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IACxB,QAAQ,CAAC,eAAe,EAAE,CAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACvE,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACtD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CACtE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,QAAQ,CAAC,qBAAqB,CAAC,GACvC,qBAAqB,CAAC,cAAc,EAAE,YAAY,CAAC,CAqFrD"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, extname, isAbsolute, resolve } from 'node:path';
|
|
3
|
+
import { logWarn } from '@flighthq/log/contract';
|
|
4
|
+
import { createRequirementCodegenPlan } from '@flighthq/requirement-codegen/contract';
|
|
5
|
+
import { DEFAULT_CONTENT_ANALYZERS } from './contentAnalyzers';
|
|
6
|
+
import { generateManifestModuleSource, MANIFEST_BACKEND_EXPORTS, MANIFEST_PARSER_BACKEND, } from './manifestModuleSource';
|
|
7
|
+
/** The import suffix that asks this plugin for a file's manifest instead of the file itself. */
|
|
8
|
+
export const MANIFEST_QUERY_SUFFIX = '?manifest';
|
|
9
|
+
/**
|
|
10
|
+
* Creates the plugin. Explicit factory, no default export and no module-level state: importing this
|
|
11
|
+
* package configures nothing.
|
|
12
|
+
*
|
|
13
|
+
* It intercepts a per-file import — `import { glOptions } from './asset.swf?manifest'` — reads THAT
|
|
14
|
+
* file, picks the analyzer by extension, resolves the requirements against the catalog for every
|
|
15
|
+
* backend, and serves a module of flat per-backend fragments. Per-file rather than project-wide
|
|
16
|
+
* because the import graph is then the truth: a document nothing imports contributes nothing, and a
|
|
17
|
+
* bundler can see which implementations each document actually pulled in.
|
|
18
|
+
*
|
|
19
|
+
* The pipeline is the SDK's own — `parse*Requirements` to read content, the catalog to resolve. This
|
|
20
|
+
* package contributes the Vite lifecycle and the module text, nothing else.
|
|
21
|
+
*/
|
|
22
|
+
export function createManifestPlugin(options) {
|
|
23
|
+
const report = options.onDiagnostic ?? ((message) => logWarn(message, MANIFEST_LOG_CHANNEL));
|
|
24
|
+
const decompressors = { deflate: options.deflate ?? null, lzma: options.lzma ?? null };
|
|
25
|
+
// Keyed by the absolute content path, so invalidation is per imported source: editing one document
|
|
26
|
+
// rebuilds that document's module and leaves every other file's cached module untouched.
|
|
27
|
+
const sourceByPath = new Map();
|
|
28
|
+
async function build(path) {
|
|
29
|
+
const extension = extname(path).toLowerCase();
|
|
30
|
+
const analyzer = DEFAULT_CONTENT_ANALYZERS[extension];
|
|
31
|
+
if (analyzer === undefined) {
|
|
32
|
+
report(`unsupported content format, no analyzer for ${extension}: ${path}`);
|
|
33
|
+
return generateManifestModuleSource([], extension).source;
|
|
34
|
+
}
|
|
35
|
+
let requirements;
|
|
36
|
+
try {
|
|
37
|
+
const source = new Uint8Array(await readFile(path));
|
|
38
|
+
// Readability is checked SEPARATELY from analysis. An analyzer reports an empty set both for a
|
|
39
|
+
// file that requires nothing and for one it could not decode, so without this probe a compressed
|
|
40
|
+
// asset silently yields an empty manifest and a bundle missing every handler it needed.
|
|
41
|
+
if (!analyzer.isReadable(source, decompressors)) {
|
|
42
|
+
report(`unreadable content, analyzed to nothing: ${path}` +
|
|
43
|
+
` — if it is compressed, pass a matching deflate/lzma capability to createManifestPlugin`);
|
|
44
|
+
return generateManifestModuleSource([], extension).source;
|
|
45
|
+
}
|
|
46
|
+
requirements = analyzer.analyze(source, decompressors);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
report(`analyzer failed for ${path}: ${error.message}`);
|
|
50
|
+
return generateManifestModuleSource([], extension).source;
|
|
51
|
+
}
|
|
52
|
+
// Resolution is the codegen kernel's job, not this plugin's. Every backend is planned, not a
|
|
53
|
+
// configured one: the module exports a fragment per backend and the application's import decides
|
|
54
|
+
// which survive the bundle. The parser is planned alongside them because parserOptions is one of
|
|
55
|
+
// those exports.
|
|
56
|
+
const rows = [];
|
|
57
|
+
const unresolvedEverywhere = new Map();
|
|
58
|
+
const resolvedSomewhere = new Set();
|
|
59
|
+
for (const backend of MANIFEST_RESOLVED_BACKENDS) {
|
|
60
|
+
const plan = createRequirementCodegenPlan(options.catalog, requirements, backend);
|
|
61
|
+
for (const entry of plan.entries) {
|
|
62
|
+
rows.push({ entry, kind: entry.kind });
|
|
63
|
+
resolvedSomewhere.add(`${entry.facet}\u0000${entry.kind}`);
|
|
64
|
+
}
|
|
65
|
+
for (const requirement of plan.unresolved) {
|
|
66
|
+
unresolvedEverywhere.set(`${requirement.facet}\u0000${requirement.key}`, requirement);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Reported only when NO backend could place it. A requirement satisfied by GL but absent from the
|
|
70
|
+
// WGPU catalog is a normal per-backend gap, not a hole in the content's coverage.
|
|
71
|
+
for (const [identity, requirement] of unresolvedEverywhere) {
|
|
72
|
+
if (resolvedSomewhere.has(identity))
|
|
73
|
+
continue;
|
|
74
|
+
report(`no catalog entry for ${requirement.facet} ${requirement.key}: ${path}`);
|
|
75
|
+
}
|
|
76
|
+
const generated = generateManifestModuleSource(rows, extension);
|
|
77
|
+
for (const problem of generated.problems)
|
|
78
|
+
report(`${problem}: ${path}`);
|
|
79
|
+
return generated.source;
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
enforce: 'pre',
|
|
83
|
+
handleHotUpdate: (context) => {
|
|
84
|
+
sourceByPath.delete(context.file);
|
|
85
|
+
},
|
|
86
|
+
load: async (id) => {
|
|
87
|
+
if (!id.startsWith(MANIFEST_RESOLVED_PREFIX))
|
|
88
|
+
return null;
|
|
89
|
+
const path = id.slice(MANIFEST_RESOLVED_PREFIX.length);
|
|
90
|
+
let source = sourceByPath.get(path);
|
|
91
|
+
if (source === undefined) {
|
|
92
|
+
source = await build(path);
|
|
93
|
+
sourceByPath.set(path, source);
|
|
94
|
+
}
|
|
95
|
+
return source;
|
|
96
|
+
},
|
|
97
|
+
name: 'flight-manifest',
|
|
98
|
+
resolveId: (id, importer) => {
|
|
99
|
+
if (!id.endsWith(MANIFEST_QUERY_SUFFIX))
|
|
100
|
+
return null;
|
|
101
|
+
const request = id.slice(0, -MANIFEST_QUERY_SUFFIX.length);
|
|
102
|
+
const base = importer === undefined ? process.cwd() : dirname(importer);
|
|
103
|
+
return `${MANIFEST_RESOLVED_PREFIX}${isAbsolute(request) ? request : resolve(base, request)}`;
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
// Rollup treats a leading NUL as "this id belongs to a plugin", which keeps the resolved id out of
|
|
108
|
+
// filesystem resolution while still carrying the path the module was built from.
|
|
109
|
+
const MANIFEST_RESOLVED_PREFIX = '\0flight-manifest:';
|
|
110
|
+
const MANIFEST_LOG_CHANNEL = 'flight-manifest';
|
|
111
|
+
const MANIFEST_RESOLVED_BACKENDS = [...Object.keys(MANIFEST_BACKEND_EXPORTS), MANIFEST_PARSER_BACKEND].sort();
|
|
112
|
+
//# sourceMappingURL=manifestPlugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifestPlugin.js","sourceRoot":"","sources":["../src/manifestPlugin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The render-state options field that collects each facet's implementations.
|
|
3
|
+
*
|
|
4
|
+
* A catalog row names a facet, a kind and an implementation, but not the options field that holds it —
|
|
5
|
+
* deliberately, because a row is a fact about ownership and should not encode one consumer's option
|
|
6
|
+
* shape. This table is where the plugin decides.
|
|
7
|
+
*
|
|
8
|
+
* `document.format` maps to `nodeRenderers` for a render backend: a document's content kinds are the
|
|
9
|
+
* nodes the importer produces, so that is the field a renderer needs them in.
|
|
10
|
+
*/
|
|
11
|
+
export declare const REQUIREMENT_OPTION_FIELDS: Readonly<Record<string, string>>;
|
|
12
|
+
/**
|
|
13
|
+
* The kind-keyed fields each backend's options type ACTUALLY declares, taken from the types package.
|
|
14
|
+
*
|
|
15
|
+
* Backend sets are not interchangeable and the differences are real: `GlRenderStateOptions` has
|
|
16
|
+
* `blendRealizations`, `pbrExtensions` and `customEffectShaders`; `WgpuRenderStateOptions` has NONE of
|
|
17
|
+
* those three. Emitting one into the other would produce a fragment that typechecks nowhere and
|
|
18
|
+
* silently does nothing when spread, so routing is per backend and a row naming a field its backend
|
|
19
|
+
* does not accept is REPORTED rather than emitted.
|
|
20
|
+
*
|
|
21
|
+
* All four backends now take ONE options object, and every set below is derived from the options type
|
|
22
|
+
* that backend actually declares rather than from a guess about which fields it ought to have.
|
|
23
|
+
* `CanvasRenderStateOptions` carries the registries and texture resolvers directly, so a canvas
|
|
24
|
+
* fragment spreads into `createCanvasRenderState(options)` like every other backend's. DOM's registry
|
|
25
|
+
* fields are seeded into the runtime at construction. Neither canvas nor DOM declares
|
|
26
|
+
* `blendRealizations`, and DOM declares no material or effect tables, so a row naming one is reported
|
|
27
|
+
* rather than emitted into a field that does not exist.
|
|
28
|
+
*/
|
|
29
|
+
export declare const BACKEND_OPTION_FIELDS: Readonly<Record<string, ReadonlySet<string>>>;
|
|
30
|
+
/** The parser options field each content format spreads its ordered handler list into. */
|
|
31
|
+
export declare const PARSER_HANDLER_FIELDS: Readonly<Record<string, string>>;
|
|
32
|
+
//# sourceMappingURL=requirementOptionFields.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requirementOptionFields.d.ts","sourceRoot":"","sources":["../src/requirementOptionFields.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,eAAO,MAAM,yBAAyB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQrE,CAAC;AAEH;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,qBAAqB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CA+B9E,CAAC;AAEH,0FAA0F;AAC1F,eAAO,MAAM,qBAAqB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIjE,CAAC"}
|