@rusveltejs/compiler 0.1.0 → 0.1.2
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/compiler.js +101 -38
- package/native.js +7 -1
- package/package.json +9 -8
- package/register.js +10 -19
package/compiler.js
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
// built by Svelte's own code. Anything rusvelte doesn't handle falls back to the real compiler:
|
|
5
5
|
// other Svelte versions, unsupported options, compile errors (so their messages and frames
|
|
6
6
|
// are Svelte's own) and internal failures.
|
|
7
|
+
import { createRequire } from 'node:module';
|
|
8
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
7
9
|
import * as native from './native.js';
|
|
8
10
|
|
|
9
11
|
const debug = !!process.env.RUSVELTE_DEBUG;
|
|
@@ -132,17 +134,54 @@ class JsSourceMap {
|
|
|
132
134
|
}
|
|
133
135
|
|
|
134
136
|
/**
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
* merge_with_preprocessor_map: Function,
|
|
140
|
-
* get_source_name: Function,
|
|
141
|
-
* MagicSourceMap: any
|
|
142
|
-
* }} svelte
|
|
137
|
+
* The internals of the installed compiler that results are built with (its `state`,
|
|
138
|
+
* `CompileDiagnostic`, source map helpers and magic-string's `SourceMap`), loaded only once the
|
|
139
|
+
* versions match: another version may lay its files out differently
|
|
140
|
+
* @param {string} real_url the URL of the installed `svelte/compiler` entry
|
|
143
141
|
*/
|
|
144
|
-
|
|
145
|
-
const
|
|
142
|
+
async function load_internals(real_url) {
|
|
143
|
+
const at = (path) => new URL(path, real_url).href;
|
|
144
|
+
const [state, { CompileDiagnostic }, { merge_with_preprocessor_map, get_source_name }, { validate_component_options }] = await Promise.all([
|
|
145
|
+
import(at('./state.js')),
|
|
146
|
+
import(at('./utils/compile_diagnostic.js')),
|
|
147
|
+
import(at('./utils/mapped_code.js')),
|
|
148
|
+
import(at('./validate-options.js'))
|
|
149
|
+
]);
|
|
150
|
+
const magic_string = createRequire(fileURLToPath(real_url)).resolve('magic-string');
|
|
151
|
+
// `require` resolves its CommonJS build, where `SourceMap` hangs off the default export
|
|
152
|
+
const magic = await import(pathToFileURL(magic_string).href);
|
|
153
|
+
const MagicSourceMap = magic.SourceMap ?? magic.default?.SourceMap;
|
|
154
|
+
for (const [name, value] of Object.entries({ CompileDiagnostic, merge_with_preprocessor_map, get_source_name, MagicSourceMap, validate_component_options })) {
|
|
155
|
+
if (typeof value !== 'function') throw new Error(`${name} is not available`);
|
|
156
|
+
}
|
|
157
|
+
if (typeof state.reset !== 'function' || typeof state.set_source !== 'function' || typeof state.adjust !== 'function') {
|
|
158
|
+
throw new Error('svelte/compiler state has an unexpected shape');
|
|
159
|
+
}
|
|
160
|
+
return { state, CompileDiagnostic, merge_with_preprocessor_map, get_source_name, MagicSourceMap, validate_component_options };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* `compile` and `compileModule` for the installed compiler: rusvelte's where it can run, the
|
|
165
|
+
* installed compiler's otherwise
|
|
166
|
+
* @param {typeof import('svelte/compiler')} real
|
|
167
|
+
* @param {string} real_url
|
|
168
|
+
*/
|
|
169
|
+
export async function create(real, real_url) {
|
|
170
|
+
const use_real = (reason) => {
|
|
171
|
+
console.warn(`[rusvelte] ${reason}; using svelte/compiler`);
|
|
172
|
+
return { compile: real.compile, compileModule: real.compileModule };
|
|
173
|
+
};
|
|
174
|
+
if (!native.native) return use_real(native.load_error);
|
|
175
|
+
if (real.VERSION !== native.svelteVersion && !process.env.RUSVELTE_ALLOW_VERSION_MISMATCH) {
|
|
176
|
+
return use_real(`svelte ${real.VERSION} is installed, but this rusvelte build reproduces svelte ${native.svelteVersion}`);
|
|
177
|
+
}
|
|
178
|
+
let internals;
|
|
179
|
+
try {
|
|
180
|
+
internals = await load_internals(real_url);
|
|
181
|
+
} catch (e) {
|
|
182
|
+
return use_real(`can't use the internals of the installed svelte/compiler (${e.message})`);
|
|
183
|
+
}
|
|
184
|
+
const { state, CompileDiagnostic, merge_with_preprocessor_map, get_source_name, MagicSourceMap, validate_component_options } = internals;
|
|
146
185
|
|
|
147
186
|
/** results rusvelte produced (not the real compiler) */
|
|
148
187
|
const native_results = new WeakSet();
|
|
@@ -151,18 +190,6 @@ export function create(svelte) {
|
|
|
151
190
|
name = 'CompileWarning';
|
|
152
191
|
}
|
|
153
192
|
|
|
154
|
-
if (!native.native) {
|
|
155
|
-
console.warn(`[rusvelte] ${native.load_error}; using svelte/compiler`);
|
|
156
|
-
return { compile: real.compile, compileModule: real.compileModule };
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const version_ok = real.VERSION === native.svelteVersion || !!process.env.RUSVELTE_ALLOW_VERSION_MISMATCH;
|
|
160
|
-
if (!version_ok) {
|
|
161
|
-
console.warn(
|
|
162
|
-
`[rusvelte] svelte ${real.VERSION} is installed, but this rusvelte build reproduces svelte ${native.svelteVersion}; using svelte/compiler`
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
193
|
/**
|
|
167
194
|
* Runs the native compiler; null means "use the real one"
|
|
168
195
|
* @param {boolean} module
|
|
@@ -170,10 +197,17 @@ export function create(svelte) {
|
|
|
170
197
|
function run(source, options, module) {
|
|
171
198
|
// `validate-options.js` defaults `rootDir` to the working directory
|
|
172
199
|
if (options.rootDir === undefined && typeof process !== 'undefined') options = { ...options, rootDir: process.cwd() };
|
|
173
|
-
|
|
200
|
+
// a lone surrogate doesn't survive the trip into Rust (its strings are valid UTF-8)
|
|
201
|
+
if (!source.isWellFormed()) return fell_back('the source has a lone surrogate'), null;
|
|
174
202
|
const bad = unsupported_option(options, module ? MODULE_OPTIONS : COMPONENT_OPTIONS);
|
|
175
203
|
if (bad) return fell_back(`option ${bad}`), null;
|
|
176
|
-
|
|
204
|
+
let result;
|
|
205
|
+
try {
|
|
206
|
+
result = JSON.parse((module ? native.compileModule : native.compile)(source, native_options(options)));
|
|
207
|
+
} catch (e) {
|
|
208
|
+
// a broken or mismatched native build: the real compiler takes over
|
|
209
|
+
return fell_back(`native error: ${e.message}`), null;
|
|
210
|
+
}
|
|
177
211
|
if (result.unsupported) return fell_back(result.unsupported), null;
|
|
178
212
|
// let the real compiler throw, so the error is exactly Svelte's
|
|
179
213
|
if (result.error) return fell_back(`compile error ${result.error.code}`), null;
|
|
@@ -181,19 +215,38 @@ export function create(svelte) {
|
|
|
181
215
|
return result;
|
|
182
216
|
}
|
|
183
217
|
|
|
184
|
-
/**
|
|
185
|
-
|
|
218
|
+
/**
|
|
219
|
+
* The result's warnings, made the way compile makes them: `state` is reset, the options are
|
|
220
|
+
* validated by the installed compiler (its deprecation warnings, once per instance), the
|
|
221
|
+
* warnings emitted before the analysis calls `state.adjust` are made with the filename as
|
|
222
|
+
* given, and the rest with the filename relative to `rootDir`. `CompileDiagnostic` fills in
|
|
223
|
+
* filename, start/end and frame from `state`. Returns null if the validation throws.
|
|
224
|
+
* @param {boolean} module
|
|
225
|
+
*/
|
|
226
|
+
function warnings_of(source, options, result, module) {
|
|
186
227
|
state.reset({ warning: options.warningFilter, filename: options.filename });
|
|
187
228
|
state.set_source(source);
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
229
|
+
if (!module) {
|
|
230
|
+
try {
|
|
231
|
+
validate_component_options(options, '');
|
|
232
|
+
} catch {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
const out = [...state.warnings];
|
|
192
237
|
const filter = options.warningFilter ?? (() => true);
|
|
193
|
-
const
|
|
194
|
-
|
|
238
|
+
const early = result.earlyWarnings ?? 0;
|
|
239
|
+
result.warnings.forEach((w, i) => {
|
|
240
|
+
if (i === early) {
|
|
241
|
+
state.adjust({ dev: !!options.dev, rootDir: options.rootDir ?? process.cwd(), runes: result.runes ?? true });
|
|
242
|
+
}
|
|
243
|
+
// the deprecation warnings come from the validation above
|
|
244
|
+
if (w.code.startsWith('options_deprecated_')) return;
|
|
195
245
|
const warning = new CompileWarning(w.code, w.message, w.position ?? undefined);
|
|
196
246
|
if (filter(warning)) out.push(warning);
|
|
247
|
+
});
|
|
248
|
+
if (result.warnings.length <= early) {
|
|
249
|
+
state.adjust({ dev: !!options.dev, rootDir: options.rootDir ?? process.cwd(), runes: result.runes ?? true });
|
|
197
250
|
}
|
|
198
251
|
return out;
|
|
199
252
|
}
|
|
@@ -233,7 +286,8 @@ export function create(svelte) {
|
|
|
233
286
|
source = remove_bom(source);
|
|
234
287
|
const result = run(source, options, false);
|
|
235
288
|
if (!result) return real.compile(source, options);
|
|
236
|
-
|
|
289
|
+
const warnings = warnings_of(source, options, result, false);
|
|
290
|
+
if (!warnings) return real.compile(source, options);
|
|
237
291
|
// `validate_component_options` defaults the filename
|
|
238
292
|
options = { ...options, filename: options.filename ?? '(unknown)' };
|
|
239
293
|
|
|
@@ -256,7 +310,6 @@ export function create(svelte) {
|
|
|
256
310
|
merge_with_preprocessor_map(css, options, css.map.sources[0]);
|
|
257
311
|
}
|
|
258
312
|
|
|
259
|
-
const warnings = warnings_of(result, options);
|
|
260
313
|
let ast;
|
|
261
314
|
const compiled = {
|
|
262
315
|
js,
|
|
@@ -265,7 +318,8 @@ export function create(svelte) {
|
|
|
265
318
|
metadata: { runes: result.runes },
|
|
266
319
|
// parsed on demand: vite-plugin-svelte doesn't read it
|
|
267
320
|
get ast() {
|
|
268
|
-
|
|
321
|
+
// compile's AST (TypeScript removed etc.); warnings are dropped so the filter isn't called twice
|
|
322
|
+
return (ast ??= real.compile(source, { ...options, generate: false, warningFilter: () => false }).ast);
|
|
269
323
|
},
|
|
270
324
|
set ast(value) {
|
|
271
325
|
ast = value;
|
|
@@ -277,19 +331,28 @@ export function create(svelte) {
|
|
|
277
331
|
|
|
278
332
|
/** @type {typeof real.compileModule} */
|
|
279
333
|
function compileModule(source, options) {
|
|
334
|
+
const result = compile_module_native(source, options);
|
|
335
|
+
if (verify && native_results.has(result)) check('module', remove_bom(source), options, result);
|
|
336
|
+
return result;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/** @type {typeof real.compileModule} */
|
|
340
|
+
function compile_module_native(source, options) {
|
|
280
341
|
source = remove_bom(source);
|
|
281
342
|
const result = run(source, options, true);
|
|
282
343
|
if (!result) return real.compileModule(source, options);
|
|
283
|
-
|
|
344
|
+
const warnings = warnings_of(source, options, result, true);
|
|
284
345
|
options = { ...options, filename: options.filename ?? '(unknown)' };
|
|
285
346
|
const js_source_name = get_source_name(options.filename, undefined, 'input.svelte.js');
|
|
286
|
-
|
|
347
|
+
const compiled = {
|
|
287
348
|
js: { code: result.js.code, map: new JsSourceMap(result.js.mappings, js_source_name, source) },
|
|
288
349
|
css: null,
|
|
289
|
-
warnings
|
|
350
|
+
warnings,
|
|
290
351
|
metadata: { runes: true },
|
|
291
352
|
ast: null
|
|
292
353
|
};
|
|
354
|
+
native_results.add(compiled);
|
|
355
|
+
return compiled;
|
|
293
356
|
}
|
|
294
357
|
|
|
295
358
|
return { compile, compileModule };
|
package/native.js
CHANGED
|
@@ -32,7 +32,13 @@ export let native = null;
|
|
|
32
32
|
export let load_error = null;
|
|
33
33
|
try {
|
|
34
34
|
native = load();
|
|
35
|
+
// a build from another rusvelte version may not have this API
|
|
36
|
+
if (typeof native.compile !== 'function' || typeof native.compileModule !== 'function' || typeof native.svelteVersion !== 'function') {
|
|
37
|
+
throw new Error('it lacks compile, compileModule or svelteVersion');
|
|
38
|
+
}
|
|
39
|
+
native.version = native.svelteVersion();
|
|
35
40
|
} catch (e) {
|
|
41
|
+
native = null;
|
|
36
42
|
const where = process.env.RUSVELTE_NATIVE ?? platform_package();
|
|
37
43
|
load_error = `no native module for ${process.platform}-${process.arch} (${where}): ${e.message.split('\n')[0]}`;
|
|
38
44
|
}
|
|
@@ -40,4 +46,4 @@ try {
|
|
|
40
46
|
export const compile = native?.compile;
|
|
41
47
|
export const compileModule = native?.compileModule;
|
|
42
48
|
/** The Svelte version whose output this build reproduces */
|
|
43
|
-
export const svelteVersion = native?.
|
|
49
|
+
export const svelteVersion = native?.version ?? null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rusveltejs/compiler",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "The Svelte 5 compiler ported to Rust, as a drop-in for svelte/compiler in Vite builds",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"svelte",
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"exports": {
|
|
22
22
|
".": "./native.js",
|
|
23
23
|
"./register": "./register.js",
|
|
24
|
-
"./vite": "./vite.js"
|
|
24
|
+
"./vite": "./vite.js",
|
|
25
|
+
"./package.json": "./package.json"
|
|
25
26
|
},
|
|
26
27
|
"files": [
|
|
27
28
|
"native.js",
|
|
@@ -39,11 +40,11 @@
|
|
|
39
40
|
"svelte": "^5.0.0"
|
|
40
41
|
},
|
|
41
42
|
"optionalDependencies": {
|
|
42
|
-
"@rusveltejs/darwin-arm64": "0.1.
|
|
43
|
-
"@rusveltejs/darwin-x64": "0.1.
|
|
44
|
-
"@rusveltejs/linux-x64-gnu": "0.1.
|
|
45
|
-
"@rusveltejs/linux-arm64-gnu": "0.1.
|
|
46
|
-
"@rusveltejs/linux-x64-musl": "0.1.
|
|
47
|
-
"@rusveltejs/win32-x64-msvc": "0.1.
|
|
43
|
+
"@rusveltejs/darwin-arm64": "0.1.2",
|
|
44
|
+
"@rusveltejs/darwin-x64": "0.1.2",
|
|
45
|
+
"@rusveltejs/linux-x64-gnu": "0.1.2",
|
|
46
|
+
"@rusveltejs/linux-arm64-gnu": "0.1.2",
|
|
47
|
+
"@rusveltejs/linux-x64-musl": "0.1.2",
|
|
48
|
+
"@rusveltejs/win32-x64-msvc": "0.1.2"
|
|
48
49
|
}
|
|
49
50
|
}
|
package/register.js
CHANGED
|
@@ -7,24 +7,20 @@
|
|
|
7
7
|
// or call `register()` before importing them dynamically (vite.js does).
|
|
8
8
|
import { registerHooks } from 'node:module';
|
|
9
9
|
|
|
10
|
-
const PACKAGE = new URL('./', import.meta.url).href;
|
|
11
10
|
const SHIM = new URL('./svelte-compiler.js', import.meta.url).href;
|
|
12
11
|
const COMPILER = new URL('./compiler.js', import.meta.url).href;
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
// one hook per process, even with several copies of this package installed: a second hook
|
|
14
|
+
// would wrap the first one's module instead of the real compiler
|
|
15
|
+
const REGISTERED = Symbol.for('@rusveltejs/compiler/register');
|
|
15
16
|
|
|
16
17
|
export function register() {
|
|
17
|
-
if (
|
|
18
|
-
|
|
18
|
+
if (globalThis[REGISTERED]) return;
|
|
19
|
+
globalThis[REGISTERED] = true;
|
|
19
20
|
registerHooks({
|
|
20
21
|
resolve(specifier, context, next) {
|
|
21
22
|
const parent = context.parentURL ?? '';
|
|
22
|
-
|
|
23
|
-
if (parent.startsWith(SHIM + '?')) {
|
|
24
|
-
const real = new URL(parent).searchParams.get('real');
|
|
25
|
-
return next(specifier, { ...context, parentURL: real });
|
|
26
|
-
}
|
|
27
|
-
if (specifier === 'svelte/compiler' && !parent.startsWith(PACKAGE) && context.conditions?.includes('import')) {
|
|
23
|
+
if (specifier === 'svelte/compiler' && !parent.startsWith(SHIM) && context.conditions?.includes('import')) {
|
|
28
24
|
const real = next(specifier, context);
|
|
29
25
|
return { url: `${SHIM}?real=${encodeURIComponent(real.url)}`, format: 'module', shortCircuit: true };
|
|
30
26
|
}
|
|
@@ -32,17 +28,12 @@ export function register() {
|
|
|
32
28
|
},
|
|
33
29
|
load(url, context, next) {
|
|
34
30
|
if (!url.startsWith(SHIM + '?')) return next(url, context);
|
|
35
|
-
const real = new URL(url).searchParams.get('real');
|
|
36
|
-
const at = (path) => JSON.stringify(new URL(path, real).href);
|
|
31
|
+
const real = JSON.stringify(new URL(url).searchParams.get('real'));
|
|
37
32
|
const source = `
|
|
38
|
-
export * from ${
|
|
39
|
-
import * as real from ${
|
|
40
|
-
import * as state from ${at('./state.js')};
|
|
41
|
-
import { CompileDiagnostic } from ${at('./utils/compile_diagnostic.js')};
|
|
42
|
-
import { merge_with_preprocessor_map, get_source_name } from ${at('./utils/mapped_code.js')};
|
|
43
|
-
import { SourceMap as MagicSourceMap } from 'magic-string';
|
|
33
|
+
export * from ${real};
|
|
34
|
+
import * as real from ${real};
|
|
44
35
|
import { create } from ${JSON.stringify(COMPILER)};
|
|
45
|
-
export const { compile, compileModule } = create(
|
|
36
|
+
export const { compile, compileModule } = await create(real, ${real});
|
|
46
37
|
`;
|
|
47
38
|
return { format: 'module', source, shortCircuit: true };
|
|
48
39
|
}
|