@rsvelte/vite-plugin-svelte-native 0.2.8 → 0.3.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/index.cjs +128 -5
- package/index.d.ts +36 -8
- package/package.json +6 -6
- package/parse-envelope.js +20 -3
package/index.cjs
CHANGED
|
@@ -51,6 +51,72 @@ try {
|
|
|
51
51
|
);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
// The NAPI boundary can't accept function values, so resolve them here: evaluate
|
|
55
|
+
// `customElement`/`css`/`runes` and hand back `warningFilter` for the caller to
|
|
56
|
+
// post-filter warnings (which never affect codegen).
|
|
57
|
+
function prepareCompileOptions(options) {
|
|
58
|
+
if (options == null) return { options, warningFilter: undefined };
|
|
59
|
+
const { customElement, css, runes, warningFilter } = options;
|
|
60
|
+
const hasParametric =
|
|
61
|
+
typeof customElement === 'function' ||
|
|
62
|
+
typeof css === 'function' ||
|
|
63
|
+
typeof runes === 'function';
|
|
64
|
+
const hasWarningFilter = typeof warningFilter === 'function';
|
|
65
|
+
if (!hasParametric && !hasWarningFilter) {
|
|
66
|
+
return { options, warningFilter: undefined };
|
|
67
|
+
}
|
|
68
|
+
// Svelte defaults `filename` to '(unknown)' before invoking these functions.
|
|
69
|
+
const meta = { filename: options.filename ?? '(unknown)' };
|
|
70
|
+
const resolved = { ...options };
|
|
71
|
+
if (typeof customElement === 'function') resolved.customElement = customElement(meta);
|
|
72
|
+
if (typeof css === 'function') resolved.css = css(meta);
|
|
73
|
+
if (typeof runes === 'function') resolved.runes = runes(meta);
|
|
74
|
+
if (hasWarningFilter) delete resolved.warningFilter;
|
|
75
|
+
return { options: resolved, warningFilter: hasWarningFilter ? warningFilter : undefined };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Port of Svelte's `hash()` (submodules/svelte/packages/svelte/src/utils.js),
|
|
79
|
+
// handed to a user `cssHash` callback so custom scope classes match upstream's digest.
|
|
80
|
+
const regexReturnCharacters = /\r/g;
|
|
81
|
+
function hash(str) {
|
|
82
|
+
str = str.replace(regexReturnCharacters, '');
|
|
83
|
+
let h = 5381;
|
|
84
|
+
let i = str.length;
|
|
85
|
+
while (i--) h = ((h << 5) - h) ^ str.charCodeAt(i);
|
|
86
|
+
return (h >>> 0).toString(36);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Adapt a user `cssHash` to the bridge shape. It must never reject: a rejected
|
|
90
|
+
// Promise crossing the NAPI boundary can crash V8 during threadsafe-function
|
|
91
|
+
// teardown — so a throw becomes `{ error }` (Rust turns it into a compile failure)
|
|
92
|
+
// and a non-string return becomes `{ value: null }` (Rust falls back to the default hash).
|
|
93
|
+
function makeCssHashCallback(userCssHash) {
|
|
94
|
+
return async (name, filename, css) => {
|
|
95
|
+
try {
|
|
96
|
+
const result = await userCssHash({ hash, css, name, filename });
|
|
97
|
+
return { value: typeof result === 'string' ? result : null };
|
|
98
|
+
} catch (err) {
|
|
99
|
+
return { error: err instanceof Error ? err.message : String(err) };
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function applyWarningFilter(result, warningFilter) {
|
|
105
|
+
if (!warningFilter || result == null) return result;
|
|
106
|
+
const warnings = result.warnings;
|
|
107
|
+
if (Array.isArray(warnings) && warnings.length) {
|
|
108
|
+
// `warnings` is a lazy getter on the envelope-decoded result; redefine it
|
|
109
|
+
// as a plain data property so the filtered array replaces the accessor.
|
|
110
|
+
Object.defineProperty(result, 'warnings', {
|
|
111
|
+
value: warnings.filter((warning) => warningFilter(warning)),
|
|
112
|
+
writable: true,
|
|
113
|
+
enumerable: true,
|
|
114
|
+
configurable: true,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
|
|
54
120
|
// `compile` / `compileModule` are wrapped to route through the
|
|
55
121
|
// raw-transfer envelope (`compileEnvelope`): the Rust side hands us
|
|
56
122
|
// one `Buffer`, the JS side lazy-decodes only the fields the caller
|
|
@@ -62,11 +128,23 @@ try {
|
|
|
62
128
|
// directly. The legacy JSON path is preserved as `compileLegacy` for
|
|
63
129
|
// parity testing and as an escape hatch.
|
|
64
130
|
function compile(source, options) {
|
|
65
|
-
|
|
131
|
+
if (typeof options?.cssHash === 'function') {
|
|
132
|
+
// A dynamic cssHash needs the Rust→JS callback bridge, which would deadlock
|
|
133
|
+
// the JS event loop on the sync path; direct the caller to `compileAsync`.
|
|
134
|
+
throw new Error(
|
|
135
|
+
'[@rsvelte/vite-plugin-svelte-native] A dynamic `cssHash` function requires the async compile path; call `compileAsync(source, options)` instead. (A constant hash can use `cssHashOverride`.)',
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
const { options: resolved, warningFilter } = prepareCompileOptions(options);
|
|
139
|
+
return applyWarningFilter(decodeEnvelope(binding.compileEnvelope(source, resolved)), warningFilter);
|
|
66
140
|
}
|
|
67
141
|
|
|
68
142
|
function compileModule(source, options) {
|
|
69
|
-
|
|
143
|
+
const { options: resolved, warningFilter } = prepareCompileOptions(options);
|
|
144
|
+
return applyWarningFilter(
|
|
145
|
+
decodeEnvelope(binding.compileModuleEnvelope(source, resolved)),
|
|
146
|
+
warningFilter,
|
|
147
|
+
);
|
|
70
148
|
}
|
|
71
149
|
|
|
72
150
|
// `compileBatch([{source, options}, …])` compiles N files in
|
|
@@ -74,8 +152,29 @@ function compileModule(source, options) {
|
|
|
74
152
|
// exactly once. The returned array is the same length as the input;
|
|
75
153
|
// each slot is either a `CompileResult` or an `Error` (parse
|
|
76
154
|
// failures don't abort the whole batch).
|
|
155
|
+
// Batch compilation runs on rayon workers with no JS event loop to service the
|
|
156
|
+
// callback, so a dynamic cssHash can't be bridged here — reject it rather than drop it.
|
|
157
|
+
function assertNoDynamicCssHash(options) {
|
|
158
|
+
if (typeof options?.cssHash === 'function') {
|
|
159
|
+
throw new Error(
|
|
160
|
+
'[@rsvelte/vite-plugin-svelte-native] A dynamic `cssHash` function is not supported in compileBatch; compile such files individually with `compileAsync`.',
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
77
165
|
function compileBatch(inputs) {
|
|
78
|
-
|
|
166
|
+
const filters = [];
|
|
167
|
+
const prepared = inputs.map((input, i) => {
|
|
168
|
+
assertNoDynamicCssHash(input.options);
|
|
169
|
+
const { options, warningFilter } = prepareCompileOptions(input.options);
|
|
170
|
+
if (warningFilter) filters[i] = warningFilter;
|
|
171
|
+
return options === input.options ? input : { source: input.source, options };
|
|
172
|
+
});
|
|
173
|
+
const results = decodeBatch(binding.compileBatch(prepared));
|
|
174
|
+
if (filters.length) {
|
|
175
|
+
results.forEach((result, i) => applyWarningFilter(result, filters[i]));
|
|
176
|
+
}
|
|
177
|
+
return results;
|
|
79
178
|
}
|
|
80
179
|
|
|
81
180
|
// `compileAsync` / `compileBatchAsync` release the JS event loop
|
|
@@ -84,11 +183,35 @@ function compileBatch(inputs) {
|
|
|
84
183
|
// (Vite middleware, SSR pre-render) — the await yields control
|
|
85
184
|
// instead of blocking V8.
|
|
86
185
|
async function compileAsync(source, options) {
|
|
87
|
-
|
|
186
|
+
const { options: resolved, warningFilter } = prepareCompileOptions(options);
|
|
187
|
+
if (typeof options?.cssHash === 'function') {
|
|
188
|
+
// The bridge entry returns a plain (JSON) CompileResult, not an envelope.
|
|
189
|
+
const result = await binding.compileWithCssHash(
|
|
190
|
+
source,
|
|
191
|
+
resolved,
|
|
192
|
+
makeCssHashCallback(options.cssHash),
|
|
193
|
+
);
|
|
194
|
+
return applyWarningFilter(result, warningFilter);
|
|
195
|
+
}
|
|
196
|
+
return applyWarningFilter(
|
|
197
|
+
decodeEnvelope(await binding.compileEnvelopeAsync(source, resolved)),
|
|
198
|
+
warningFilter,
|
|
199
|
+
);
|
|
88
200
|
}
|
|
89
201
|
|
|
90
202
|
async function compileBatchAsync(inputs) {
|
|
91
|
-
|
|
203
|
+
const filters = [];
|
|
204
|
+
const prepared = inputs.map((input, i) => {
|
|
205
|
+
assertNoDynamicCssHash(input.options);
|
|
206
|
+
const { options, warningFilter } = prepareCompileOptions(input.options);
|
|
207
|
+
if (warningFilter) filters[i] = warningFilter;
|
|
208
|
+
return options === input.options ? input : { source: input.source, options };
|
|
209
|
+
});
|
|
210
|
+
const results = decodeBatch(await binding.compileBatchAsync(prepared));
|
|
211
|
+
if (filters.length) {
|
|
212
|
+
results.forEach((result, i) => applyWarningFilter(result, filters[i]));
|
|
213
|
+
}
|
|
214
|
+
return results;
|
|
92
215
|
}
|
|
93
216
|
|
|
94
217
|
// Re-export every NAPI function as its own named binding so node's
|
package/index.d.ts
CHANGED
|
@@ -22,29 +22,56 @@ export interface CompileOptions {
|
|
|
22
22
|
rootDir?: string;
|
|
23
23
|
/** Component identifier hint. */
|
|
24
24
|
name?: string;
|
|
25
|
-
/**
|
|
26
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Compile as a custom element. A function form `({ filename }) => boolean`
|
|
27
|
+
* is evaluated once at the binding boundary.
|
|
28
|
+
*/
|
|
29
|
+
customElement?: boolean | ((meta: { filename: string }) => boolean);
|
|
27
30
|
/** Generate `accessors`. */
|
|
28
31
|
accessors?: boolean;
|
|
29
32
|
/** HTML namespace. */
|
|
30
33
|
namespace?: 'html' | 'svg' | 'mathml';
|
|
31
34
|
/** Hint that bindings are immutable. */
|
|
32
35
|
immutable?: boolean;
|
|
33
|
-
/**
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Output CSS injected into the bundle or as an external asset. A function
|
|
38
|
+
* form `({ filename }) => 'injected' | 'external'` is evaluated once at the
|
|
39
|
+
* binding boundary.
|
|
40
|
+
*/
|
|
41
|
+
css?:
|
|
42
|
+
| 'injected'
|
|
43
|
+
| 'external'
|
|
44
|
+
| ((meta: { filename: string }) => 'injected' | 'external');
|
|
45
|
+
/**
|
|
46
|
+
* Custom hash function for CSS scoping. Called once per component with the
|
|
47
|
+
* component's CSS; the returned string becomes the scope class. Because it
|
|
48
|
+
* depends on the CSS, it is bridged into the compiler through a threadsafe
|
|
49
|
+
* callback and is only supported on the async path — use `compileAsync`.
|
|
50
|
+
* The synchronous `compile` throws if given a `cssHash` function; pass a
|
|
51
|
+
* pre-computed constant via `cssHashOverride` instead.
|
|
52
|
+
*/
|
|
36
53
|
cssHash?: (args: {
|
|
37
54
|
hash: (input: string) => string;
|
|
38
55
|
css: string;
|
|
39
56
|
name: string;
|
|
40
|
-
filename: string
|
|
57
|
+
filename: string;
|
|
41
58
|
}) => string;
|
|
59
|
+
/**
|
|
60
|
+
* Pre-computed constant CSS scope hash. Escape hatch for callers (e.g.
|
|
61
|
+
* `@rsvelte/vite-plugin-svelte`'s HMR path) that already know the hash and
|
|
62
|
+
* want to skip the `cssHash` callback bridge entirely.
|
|
63
|
+
*/
|
|
64
|
+
cssHashOverride?: string;
|
|
42
65
|
/** Preserve HTML comments in output. */
|
|
43
66
|
preserveComments?: boolean;
|
|
44
67
|
/** Preserve whitespace in the template. */
|
|
45
68
|
preserveWhitespace?: boolean;
|
|
46
|
-
/**
|
|
47
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Force runes mode (`true`), legacy (`false`), or auto-detect (`undefined`).
|
|
71
|
+
* A function form `({ filename }) => boolean | undefined` is evaluated once
|
|
72
|
+
* at the binding boundary.
|
|
73
|
+
*/
|
|
74
|
+
runes?: boolean | ((meta: { filename: string }) => boolean | undefined);
|
|
48
75
|
/** Disclose the compiler version in the output banner. */
|
|
49
76
|
discloseVersion?: boolean;
|
|
50
77
|
/** Source-map options (forwarded through magic-string). */
|
|
@@ -70,6 +97,7 @@ export interface ModuleCompileOptions {
|
|
|
70
97
|
generate?: 'client' | 'server' | false;
|
|
71
98
|
filename?: string;
|
|
72
99
|
rootDir?: string;
|
|
100
|
+
/** Filter compiler warnings (applied to the returned `warnings` array). */
|
|
73
101
|
warningFilter?: (warning: Warning) => boolean;
|
|
74
102
|
}
|
|
75
103
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rsvelte/vite-plugin-svelte-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "NAPI bindings to the rsvelte compiler — used by the @rsvelte vite-plugin-svelte shim",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -29,11 +29,11 @@
|
|
|
29
29
|
"index.d.ts"
|
|
30
30
|
],
|
|
31
31
|
"optionalDependencies": {
|
|
32
|
-
"@rsvelte/vite-plugin-svelte-native-darwin-arm64": "^0.
|
|
33
|
-
"@rsvelte/vite-plugin-svelte-native-darwin-x64": "^0.
|
|
34
|
-
"@rsvelte/vite-plugin-svelte-native-linux-arm64-gnu": "^0.
|
|
35
|
-
"@rsvelte/vite-plugin-svelte-native-linux-x64-gnu": "^0.
|
|
36
|
-
"@rsvelte/vite-plugin-svelte-native-win32-x64-msvc": "^0.
|
|
32
|
+
"@rsvelte/vite-plugin-svelte-native-darwin-arm64": "^0.3.0",
|
|
33
|
+
"@rsvelte/vite-plugin-svelte-native-darwin-x64": "^0.3.0",
|
|
34
|
+
"@rsvelte/vite-plugin-svelte-native-linux-arm64-gnu": "^0.3.0",
|
|
35
|
+
"@rsvelte/vite-plugin-svelte-native-linux-x64-gnu": "^0.3.0",
|
|
36
|
+
"@rsvelte/vite-plugin-svelte-native-win32-x64-msvc": "^0.3.0"
|
|
37
37
|
},
|
|
38
38
|
"publishConfig": {
|
|
39
39
|
"access": "public"
|
package/parse-envelope.js
CHANGED
|
@@ -15,7 +15,11 @@
|
|
|
15
15
|
const { isWindowOutOfBounds, windowOutOfBoundsMessage } = require('./lib/bounds-check.js');
|
|
16
16
|
|
|
17
17
|
const MAGIC = 0x3156_5052; // "RPV1" little-endian
|
|
18
|
-
|
|
18
|
+
// Bumped for the function-node AST-fidelity changes (FunctionExpression field
|
|
19
|
+
// reorder, `typeParameters` on function-like nodes, Identifier `optional`);
|
|
20
|
+
// v4 adds the object-method `typeParameters`-after-`body` flag byte.
|
|
21
|
+
// Keep in lockstep with `napi_raw_parse.rs`'s `VERSION`.
|
|
22
|
+
const VERSION = 4;
|
|
19
23
|
const HEADER_LEN = 24;
|
|
20
24
|
|
|
21
25
|
// Tags — must mirror napi_raw_parse.rs.
|
|
@@ -698,9 +702,11 @@ function readOptNode(ctx) {
|
|
|
698
702
|
function readJsIdentifier(ctx, start, end) {
|
|
699
703
|
const loc = readTypedLoc(ctx);
|
|
700
704
|
const name = readStr(ctx);
|
|
705
|
+
const optional = readBool(ctx);
|
|
701
706
|
const node = { type: 'Identifier', start, end };
|
|
702
707
|
if (loc !== null) node.loc = loc;
|
|
703
708
|
node.name = name;
|
|
709
|
+
if (optional) node.optional = true;
|
|
704
710
|
const typeAnnotation = readOptTypeAnnotation(ctx);
|
|
705
711
|
if (typeAnnotation !== null) node.typeAnnotation = typeAnnotation;
|
|
706
712
|
return node;
|
|
@@ -809,19 +815,24 @@ function readJsNewExpression(ctx, start, end) {
|
|
|
809
815
|
function readJsFunctionExpression(ctx, start, end) {
|
|
810
816
|
const loc = readTypedLoc(ctx);
|
|
811
817
|
const id = readOptNode(ctx);
|
|
818
|
+
const expression = readBool(ctx);
|
|
812
819
|
const generator = readBool(ctx);
|
|
813
820
|
const asyncFlag = readBool(ctx);
|
|
814
|
-
const
|
|
821
|
+
const typeParameters = readOptTypeAnnotation(ctx);
|
|
815
822
|
const params = readChildArray(ctx);
|
|
816
823
|
const body = readOptNode(ctx);
|
|
824
|
+
// Object-method inner functions carry `typeParameters` after `body` (acorn).
|
|
825
|
+
const typeParametersAfterBody = readBool(ctx);
|
|
817
826
|
const node = { type: 'FunctionExpression', start, end };
|
|
818
827
|
if (loc !== null) node.loc = loc;
|
|
819
828
|
node.id = id;
|
|
829
|
+
node.expression = expression;
|
|
820
830
|
node.generator = generator;
|
|
821
831
|
node.async = asyncFlag;
|
|
822
|
-
node.
|
|
832
|
+
if (typeParameters !== null && !typeParametersAfterBody) node.typeParameters = typeParameters;
|
|
823
833
|
node.params = params;
|
|
824
834
|
node.body = body;
|
|
835
|
+
if (typeParameters !== null && typeParametersAfterBody) node.typeParameters = typeParameters;
|
|
825
836
|
return node;
|
|
826
837
|
}
|
|
827
838
|
|
|
@@ -846,6 +857,7 @@ function readJsArrowFunctionExpression(ctx, start, end) {
|
|
|
846
857
|
const asyncFlag = readBool(ctx);
|
|
847
858
|
const params = readChildArray(ctx);
|
|
848
859
|
const body = readNode(ctx);
|
|
860
|
+
const typeParameters = readOptTypeAnnotation(ctx);
|
|
849
861
|
const node = { type: 'ArrowFunctionExpression', start, end };
|
|
850
862
|
if (loc !== null) node.loc = loc;
|
|
851
863
|
node.id = id;
|
|
@@ -854,6 +866,7 @@ function readJsArrowFunctionExpression(ctx, start, end) {
|
|
|
854
866
|
node.async = asyncFlag;
|
|
855
867
|
node.params = params;
|
|
856
868
|
node.body = body;
|
|
869
|
+
if (typeParameters !== null) node.typeParameters = typeParameters;
|
|
857
870
|
return node;
|
|
858
871
|
}
|
|
859
872
|
|
|
@@ -1170,15 +1183,19 @@ function readJsVariableDeclarator(ctx, start, end) {
|
|
|
1170
1183
|
function readJsFunctionDeclaration(ctx, start, end) {
|
|
1171
1184
|
const loc = readTypedLoc(ctx);
|
|
1172
1185
|
const id = readOptNode(ctx);
|
|
1186
|
+
const expression = readBool(ctx);
|
|
1173
1187
|
const generator = readBool(ctx);
|
|
1174
1188
|
const asyncFlag = readBool(ctx);
|
|
1189
|
+
const typeParameters = readOptTypeAnnotation(ctx);
|
|
1175
1190
|
const params = readChildArray(ctx);
|
|
1176
1191
|
const body = readOptNode(ctx);
|
|
1177
1192
|
const node = { type: 'FunctionDeclaration', start, end };
|
|
1178
1193
|
if (loc !== null) node.loc = loc;
|
|
1179
1194
|
node.id = id;
|
|
1195
|
+
node.expression = expression;
|
|
1180
1196
|
node.generator = generator;
|
|
1181
1197
|
node.async = asyncFlag;
|
|
1198
|
+
if (typeParameters !== null) node.typeParameters = typeParameters;
|
|
1182
1199
|
node.params = params;
|
|
1183
1200
|
node.body = body;
|
|
1184
1201
|
return node;
|