@sveltejs/kit 2.3.5 → 2.4.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "2.3.5",
3
+ "version": "2.4.1",
4
4
  "description": "The fastest way to build Svelte apps",
5
5
  "repository": {
6
6
  "type": "git",
@@ -11,6 +11,7 @@ import { get_env } from '../../exports/vite/utils.js';
11
11
  import generate_fallback from '../postbuild/fallback.js';
12
12
  import { write } from '../sync/utils.js';
13
13
  import { list_files } from '../utils.js';
14
+ import { find_server_assets } from '../generate_manifest/find_server_assets.js';
14
15
 
15
16
  const pipe = promisify(pipeline);
16
17
  const extensions = ['.html', '.js', '.mjs', '.json', '.css', '.svg', '.xml', '.wasm'];
@@ -145,6 +146,13 @@ export function create_builder({
145
146
  }
146
147
  },
147
148
 
149
+ findServerAssets(route_data) {
150
+ return find_server_assets(
151
+ build_data,
152
+ route_data.map((route) => /** @type {import('types').RouteData} */ (lookup.get(route)))
153
+ );
154
+ },
155
+
148
156
  async generateFallback(dest) {
149
157
  const manifest_path = `${config.kit.outDir}/output/server/manifest-full.js`;
150
158
  const env = get_env(config.kit.env, vite_config.mode);
@@ -0,0 +1,52 @@
1
+ import { find_deps } from '../../exports/vite/build/utils.js';
2
+
3
+ /**
4
+ * Finds all the assets that are imported by server files associated with `routes`
5
+ * @param {import('types').BuildData} build_data
6
+ * @param {import('types').RouteData[]} routes
7
+ */
8
+ export function find_server_assets(build_data, routes) {
9
+ /**
10
+ * All nodes actually used in the routes definition (prerendered routes are omitted).
11
+ * Root layout/error is always included as they are needed for 404 and root errors.
12
+ * @type {Set<any>}
13
+ */
14
+ const used_nodes = new Set([0, 1]);
15
+
16
+ // TODO add hooks.server.js asset imports
17
+ /** @type {Set<string>} */
18
+ const server_assets = new Set();
19
+
20
+ /** @param {string} id */
21
+ function add_assets(id) {
22
+ if (id in build_data.server_manifest) {
23
+ const deps = find_deps(build_data.server_manifest, id, false);
24
+ for (const asset of deps.assets) {
25
+ server_assets.add(asset);
26
+ }
27
+ }
28
+ }
29
+
30
+ for (const route of routes) {
31
+ if (route.page) {
32
+ for (const i of route.page.layouts) used_nodes.add(i);
33
+ for (const i of route.page.errors) used_nodes.add(i);
34
+ used_nodes.add(route.page.leaf);
35
+ }
36
+
37
+ if (route.endpoint) {
38
+ add_assets(route.endpoint.file);
39
+ }
40
+ }
41
+
42
+ for (const n of used_nodes) {
43
+ const node = build_data.manifest_data.nodes[n];
44
+ if (node?.server) add_assets(node.server);
45
+ }
46
+
47
+ if (build_data.manifest_data.hooks.server) {
48
+ add_assets(build_data.manifest_data.hooks.server);
49
+ }
50
+
51
+ return Array.from(server_assets);
52
+ }
@@ -1,9 +1,13 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import * as mime from 'mrmime';
1
4
  import { s } from '../../utils/misc.js';
2
5
  import { get_mime_lookup } from '../utils.js';
3
6
  import { resolve_symlinks } from '../../exports/vite/build/utils.js';
4
7
  import { compact } from '../../utils/array.js';
5
8
  import { join_relative } from '../../utils/filesystem.js';
6
9
  import { dedent } from '../sync/utils.js';
10
+ import { find_server_assets } from './find_server_assets.js';
7
11
 
8
12
  /**
9
13
  * Generates the data used to write the server-side manifest.js file. This data is used in the Vite
@@ -26,6 +30,8 @@ export function generate_manifest({ build_data, relative_path, routes }) {
26
30
  */
27
31
  const used_nodes = new Set([0, 1]);
28
32
 
33
+ const server_assets = find_server_assets(build_data, routes);
34
+
29
35
  for (const route of routes) {
30
36
  if (route.page) {
31
37
  for (const i of route.page.layouts) used_nodes.add(i);
@@ -63,6 +69,17 @@ export function generate_manifest({ build_data, relative_path, routes }) {
63
69
  return `[${string},]`;
64
70
  }
65
71
 
72
+ const mime_types = get_mime_lookup(build_data.manifest_data);
73
+
74
+ /** @type {Record<string, number>} */
75
+ const files = {};
76
+ for (const file of server_assets) {
77
+ files[file] = fs.statSync(path.resolve(build_data.out_dir, 'server', file)).size;
78
+
79
+ const ext = path.extname(file);
80
+ mime_types[ext] ??= mime.lookup(ext) || '';
81
+ }
82
+
66
83
  // prettier-ignore
67
84
  // String representation of
68
85
  /** @template {import('@sveltejs/kit').SSRManifest} T */
@@ -71,7 +88,7 @@ export function generate_manifest({ build_data, relative_path, routes }) {
71
88
  appDir: ${s(build_data.app_dir)},
72
89
  appPath: ${s(build_data.app_path)},
73
90
  assets: new Set(${s(assets)}),
74
- mimeTypes: ${s(get_mime_lookup(build_data.manifest_data))},
91
+ mimeTypes: ${s(mime_types)},
75
92
  _: {
76
93
  client: ${s(build_data.client)},
77
94
  nodes: [
@@ -80,7 +97,7 @@ export function generate_manifest({ build_data, relative_path, routes }) {
80
97
  routes: [
81
98
  ${routes.map(route => {
82
99
  if (!route.page && !route.endpoint) return;
83
-
100
+
84
101
  route.params.forEach(param => {
85
102
  if (param.matcher) matchers.add(param.matcher);
86
103
  });
@@ -98,11 +115,12 @@ export function generate_manifest({ build_data, relative_path, routes }) {
98
115
  ],
99
116
  matchers: async () => {
100
117
  ${Array.from(
101
- matchers,
118
+ matchers,
102
119
  type => `const { match: ${type} } = await import ('${(join_relative(relative_path, `/entries/matchers/${type}.js`))}')`
103
120
  ).join('\n')}
104
121
  return { ${Array.from(matchers).join(', ')} };
105
- }
122
+ },
123
+ server_assets: ${s(files)}
106
124
  }
107
125
  }
108
126
  `;
@@ -14,16 +14,22 @@ import { installPolyfills } from '../../exports/node/polyfills.js';
14
14
  import { ENDPOINT_METHODS } from '../../constants.js';
15
15
  import { filter_private_env, filter_public_env } from '../../utils/env.js';
16
16
  import { resolve_route } from '../../utils/routing.js';
17
+ import { get_page_config } from '../../utils/route_config.js';
18
+ import { check_feature } from '../../utils/features.js';
19
+ import { createReadableStream } from '@sveltejs/kit/node';
17
20
 
18
21
  export default forked(import.meta.url, analyse);
19
22
 
20
23
  /**
21
24
  * @param {{
22
25
  * manifest_path: string;
26
+ * manifest_data: import('types').ManifestData;
27
+ * server_manifest: import('vite').Manifest;
28
+ * tracked_features: Record<string, string[]>;
23
29
  * env: Record<string, string>
24
30
  * }} opts
25
31
  */
26
- async function analyse({ manifest_path, env }) {
32
+ async function analyse({ manifest_path, manifest_data, server_manifest, tracked_features, env }) {
27
33
  /** @type {import('@sveltejs/kit').SSRManifest} */
28
34
  const manifest = (await import(pathToFileURL(manifest_path).href)).manifest;
29
35
 
@@ -48,6 +54,8 @@ async function analyse({ manifest_path, env }) {
48
54
  internal.set_private_env(private_env);
49
55
  internal.set_public_env(public_env);
50
56
  internal.set_safe_public_env(public_env);
57
+ internal.set_manifest(manifest);
58
+ internal.set_read_implementation((file) => createReadableStream(`${server_root}/server/${file}`));
51
59
 
52
60
  /** @type {import('types').ServerMetadata} */
53
61
  const metadata = {
@@ -89,12 +97,26 @@ async function analyse({ manifest_path, env }) {
89
97
  }
90
98
  }
91
99
 
100
+ const route_config = page?.config ?? endpoint?.config ?? {};
101
+ const prerender = page?.prerender ?? endpoint?.prerender;
102
+
103
+ if (prerender !== true) {
104
+ for (const feature of list_features(
105
+ route,
106
+ manifest_data,
107
+ server_manifest,
108
+ tracked_features
109
+ )) {
110
+ check_feature(route.id, route_config, feature, config.adapter);
111
+ }
112
+ }
113
+
92
114
  const page_methods = page?.methods ?? [];
93
115
  const api_methods = endpoint?.methods ?? [];
94
116
  const entries = page?.entries ?? endpoint?.entries;
95
117
 
96
118
  metadata.routes.set(route.id, {
97
- config: page?.config ?? endpoint?.config,
119
+ config: route_config,
98
120
  methods: Array.from(new Set([...page_methods, ...api_methods])),
99
121
  page: {
100
122
  methods: page_methods
@@ -102,7 +124,7 @@ async function analyse({ manifest_path, env }) {
102
124
  api: {
103
125
  methods: api_methods
104
126
  },
105
- prerender: page?.prerender ?? endpoint?.prerender,
127
+ prerender,
106
128
  entries:
107
129
  entries && (await entries()).map((entry_object) => resolve_route(route.id, entry_object))
108
130
  });
@@ -163,7 +185,7 @@ function analyse_page(layouts, leaf) {
163
185
  validate_page_exports(leaf.universal, leaf.universal_id);
164
186
 
165
187
  return {
166
- config: get_config([...layouts, leaf]),
188
+ config: get_page_config([...layouts, leaf]),
167
189
  entries: leaf.universal?.entries ?? leaf.server?.entries,
168
190
  methods,
169
191
  prerender: get_option([...layouts, leaf], 'prerender') ?? false
@@ -171,22 +193,51 @@ function analyse_page(layouts, leaf) {
171
193
  }
172
194
 
173
195
  /**
174
- * Do a shallow merge (first level) of the config object
175
- * @param {Array<import('types').SSRNode | undefined>} nodes
196
+ * @param {import('types').SSRRoute} route
197
+ * @param {import('types').ManifestData} manifest_data
198
+ * @param {import('vite').Manifest} server_manifest
199
+ * @param {Record<string, string[]>} tracked_features
176
200
  */
177
- function get_config(nodes) {
178
- /** @type {any} */
179
- let current = {};
201
+ function list_features(route, manifest_data, server_manifest, tracked_features) {
202
+ const features = new Set();
180
203
 
181
- for (const node of nodes) {
182
- if (!node?.universal?.config && !node?.server?.config) continue;
204
+ const route_data = /** @type {import('types').RouteData} */ (
205
+ manifest_data.routes.find((r) => r.id === route.id)
206
+ );
183
207
 
184
- current = {
185
- ...current,
186
- ...node?.universal?.config,
187
- ...node?.server?.config
188
- };
208
+ /** @param {string} id */
209
+ function visit(id) {
210
+ const chunk = server_manifest[id];
211
+ if (!chunk) return;
212
+
213
+ if (chunk.file in tracked_features) {
214
+ for (const feature of tracked_features[chunk.file]) {
215
+ features.add(feature);
216
+ }
217
+ }
218
+
219
+ if (chunk.imports) {
220
+ for (const id of chunk.imports) {
221
+ visit(id);
222
+ }
223
+ }
224
+ }
225
+
226
+ let page_node = route_data?.leaf;
227
+ while (page_node) {
228
+ if (page_node.server) visit(page_node.server);
229
+ page_node = page_node.parent ?? null;
230
+ }
231
+
232
+ if (route_data.endpoint) {
233
+ visit(route_data.endpoint.file);
234
+ }
235
+
236
+ if (manifest_data.hooks.server) {
237
+ // TODO if hooks.server.js imports `read`, it will be in the entry chunk
238
+ // we don't currently account for that case
239
+ visit(manifest_data.hooks.server);
189
240
  }
190
241
 
191
- return Object.keys(current).length ? current : undefined;
242
+ return Array.from(features);
192
243
  }
@@ -12,6 +12,7 @@ import { queue } from './queue.js';
12
12
  import { crawl } from './crawl.js';
13
13
  import { forked } from '../../utils/fork.js';
14
14
  import * as devalue from 'devalue';
15
+ import { createReadableStream } from '@sveltejs/kit/node';
15
16
 
16
17
  export default forked(import.meta.url, prerender);
17
18
 
@@ -430,7 +431,10 @@ async function prerender({ out, manifest_path, metadata, verbose, env }) {
430
431
  log.info('Prerendering');
431
432
 
432
433
  const server = new Server(manifest);
433
- await server.init({ env });
434
+ await server.init({
435
+ env,
436
+ read: (file) => createReadableStream(`${config.outDir}/output/server/${file}`)
437
+ });
434
438
 
435
439
  for (const entry of config.prerender.entries) {
436
440
  if (entry === '*') {
@@ -3,7 +3,7 @@ import path from 'node:path';
3
3
  import colors from 'kleur';
4
4
  import { lookup } from 'mrmime';
5
5
  import { list_files, runtime_directory } from '../../utils.js';
6
- import { posixify } from '../../../utils/filesystem.js';
6
+ import { posixify, resolve_entry } from '../../../utils/filesystem.js';
7
7
  import { parse_route_id } from '../../../utils/routing.js';
8
8
  import { sort_routes } from './sort.js';
9
9
 
@@ -22,6 +22,7 @@ export default function create_manifest_data({
22
22
  cwd = process.cwd()
23
23
  }) {
24
24
  const assets = create_assets(config);
25
+ const hooks = create_hooks(config, cwd);
25
26
  const matchers = create_matchers(config, cwd);
26
27
  const { nodes, routes } = create_routes_and_nodes(cwd, config, fallback);
27
28
 
@@ -35,6 +36,7 @@ export default function create_manifest_data({
35
36
 
36
37
  return {
37
38
  assets,
39
+ hooks,
38
40
  matchers,
39
41
  nodes,
40
42
  routes
@@ -52,6 +54,22 @@ export function create_assets(config) {
52
54
  }));
53
55
  }
54
56
 
57
+ /**
58
+ * @param {import('types').ValidatedConfig} config
59
+ * @param {string} cwd
60
+ */
61
+ function create_hooks(config, cwd) {
62
+ const client = resolve_entry(config.kit.files.hooks.client);
63
+ const server = resolve_entry(config.kit.files.hooks.server);
64
+ const universal = resolve_entry(config.kit.files.hooks.universal);
65
+
66
+ return {
67
+ client: client && posixify(path.relative(cwd, client)),
68
+ server: server && posixify(path.relative(cwd, server)),
69
+ universal: universal && posixify(path.relative(cwd, universal))
70
+ };
71
+ }
72
+
55
73
  /**
56
74
  * @param {import('types').ValidatedConfig} config
57
75
  * @param {string} cwd
@@ -30,6 +30,7 @@ const server_template = ({
30
30
  import root from '../root.${isSvelte5Plus() ? 'js' : 'svelte'}';
31
31
  import { set_building, set_prerendering } from '__sveltekit/environment';
32
32
  import { set_assets } from '__sveltekit/paths';
33
+ import { set_manifest, set_read_implementation } from '__sveltekit/server';
33
34
  import { set_private_env, set_public_env, set_safe_public_env } from '${runtime_directory}/shared-server.js';
34
35
 
35
36
  export const options = {
@@ -68,7 +69,7 @@ export async function get_hooks() {
68
69
  };
69
70
  }
70
71
 
71
- export { set_assets, set_building, set_prerendering, set_private_env, set_public_env, set_safe_public_env };
72
+ export { set_assets, set_building, set_manifest, set_prerendering, set_private_env, set_public_env, set_read_implementation, set_safe_public_env };
72
73
  `;
73
74
 
74
75
  // TODO need to re-run this whenever src/app.html or src/error.html are
@@ -1,3 +1,5 @@
1
+ import { createReadStream } from 'node:fs';
2
+ import { Readable } from 'node:stream';
1
3
  import * as set_cookie_parser from 'set-cookie-parser';
2
4
  import { SvelteKitError } from '../../runtime/control.js';
3
5
 
@@ -189,3 +191,13 @@ export async function setResponse(res, response) {
189
191
  }
190
192
  }
191
193
  }
194
+
195
+ /**
196
+ * Converts a file on disk to a readable stream
197
+ * @param {string} file
198
+ * @returns {ReadableStream}
199
+ * @since 2.4.0
200
+ */
201
+ export function createReadableStream(file) {
202
+ return /** @type {ReadableStream} */ (Readable.toWeb(createReadStream(file)));
203
+ }
@@ -35,6 +35,16 @@ export interface Adapter {
35
35
  * @param builder An object provided by SvelteKit that contains methods for adapting the app
36
36
  */
37
37
  adapt(builder: Builder): MaybePromise<void>;
38
+ /**
39
+ * Checks called during dev and build to determine whether specific features will work in production with this adapter
40
+ */
41
+ supports?: {
42
+ /**
43
+ * Test support for `read` from `$app/server`
44
+ * @param config The merged route config
45
+ */
46
+ read?: (details: { config: any; route: { id: string } }) => boolean;
47
+ };
38
48
  }
39
49
 
40
50
  export type LoadProperties<input extends Record<string, any> | void> = input extends void
@@ -89,6 +99,7 @@ export interface Builder {
89
99
  /** An array of all routes (including prerendered) */
90
100
  routes: RouteDefinition[];
91
101
 
102
+ // TODO 3.0 remove this method
92
103
  /**
93
104
  * Create separate functions that map to one or more routes of your app.
94
105
  * @param fn A function that groups a set of routes into an entry point
@@ -96,6 +107,11 @@ export interface Builder {
96
107
  */
97
108
  createEntries(fn: (route: RouteDefinition) => AdapterEntry): Promise<void>;
98
109
 
110
+ /**
111
+ * Find all the assets imported by server files belonging to `routes`
112
+ */
113
+ findServerAssets(routes: RouteDefinition[]): string[];
114
+
99
115
  /**
100
116
  * Generate a fallback page for a static webserver to use when no route is matched. Useful for single-page apps.
101
117
  */
@@ -1144,7 +1160,10 @@ export class Server {
1144
1160
  }
1145
1161
 
1146
1162
  export interface ServerInitOptions {
1163
+ /** A map of environment variables */
1147
1164
  env: Record<string, string>;
1165
+ /** A function that turns an asset filename into a `ReadableStream`. Required for the `read` export from `$app/server` to work */
1166
+ read?: (file: string) => ReadableStream;
1148
1167
  }
1149
1168
 
1150
1169
  export interface SSRManifest {
@@ -1159,6 +1178,8 @@ export interface SSRManifest {
1159
1178
  nodes: SSRNodeLoader[];
1160
1179
  routes: SSRRoute[];
1161
1180
  matchers(): Promise<Record<string, ParamMatcher>>;
1181
+ /** A `[file]: size` map of all assets imported by server code */
1182
+ server_assets: Record<string, number>;
1162
1183
  };
1163
1184
  }
1164
1185
 
@@ -20,7 +20,7 @@ export function find_deps(manifest, entry, add_dynamic_css) {
20
20
  const stylesheets = new Set();
21
21
 
22
22
  /** @type {Set<string>} */
23
- const fonts = new Set();
23
+ const imported_assets = new Set();
24
24
 
25
25
  /**
26
26
  * @param {string} current
@@ -36,9 +36,7 @@ export function find_deps(manifest, entry, add_dynamic_css) {
36
36
 
37
37
  if (chunk.assets) {
38
38
  for (const asset of chunk.assets) {
39
- if (/\.(woff2?|ttf|otf)$/.test(asset)) {
40
- fonts.add(asset);
41
- }
39
+ imported_assets.add(asset);
42
40
  }
43
41
  }
44
42
 
@@ -59,11 +57,15 @@ export function find_deps(manifest, entry, add_dynamic_css) {
59
57
 
60
58
  traverse(file, true);
61
59
 
60
+ const assets = Array.from(imported_assets);
61
+
62
62
  return {
63
+ assets,
63
64
  file: chunk.file,
64
65
  imports: Array.from(imports),
65
66
  stylesheets: Array.from(stylesheets),
66
- fonts: Array.from(fonts)
67
+ // TODO do we need this separately?
68
+ fonts: assets.filter((asset) => /\.(woff2?|ttf|otf)$/.test(asset))
67
69
  };
68
70
  }
69
71
 
@@ -1,10 +1,11 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { URL } from 'node:url';
4
+ import { AsyncLocalStorage } from 'node:async_hooks';
4
5
  import colors from 'kleur';
5
6
  import sirv from 'sirv';
6
7
  import { isCSSRequest, loadEnv, buildErrorMessage } from 'vite';
7
- import { getRequest, setResponse } from '../../../exports/node/index.js';
8
+ import { createReadableStream, getRequest, setResponse } from '../../../exports/node/index.js';
8
9
  import { installPolyfills } from '../../../exports/node/polyfills.js';
9
10
  import { coalesce_to_error } from '../../../utils/error.js';
10
11
  import { posixify, resolve_entry, to_fs } from '../../../utils/filesystem.js';
@@ -15,6 +16,7 @@ import { get_mime_lookup, runtime_base } from '../../../core/utils.js';
15
16
  import { compact } from '../../../utils/array.js';
16
17
  import { not_found } from '../utils.js';
17
18
  import { SCHEME } from '../../../utils/url.js';
19
+ import { check_feature } from '../../../utils/features.js';
18
20
 
19
21
  const cwd = process.cwd();
20
22
 
@@ -27,6 +29,15 @@ const cwd = process.cwd();
27
29
  export async function dev(vite, vite_config, svelte_config) {
28
30
  installPolyfills();
29
31
 
32
+ const async_local_storage = new AsyncLocalStorage();
33
+
34
+ globalThis.__SVELTEKIT_TRACK__ = (label) => {
35
+ const context = async_local_storage.getStore();
36
+ if (!context || context.prerender === true) return;
37
+
38
+ check_feature(context.event.route.id, context.config, label, svelte_config.kit.adapter);
39
+ };
40
+
30
41
  const fetch = globalThis.fetch;
31
42
  globalThis.fetch = (info, init) => {
32
43
  if (typeof info === 'string' && !SCHEME.test(info)) {
@@ -123,6 +134,13 @@ export async function dev(vite, vite_config, svelte_config) {
123
134
  fonts: [],
124
135
  uses_env_dynamic_public: true
125
136
  },
137
+ server_assets: new Proxy(
138
+ {},
139
+ {
140
+ has: (_, /** @type {string} */ file) => fs.existsSync(file.replace(/^\/@fs/, '')),
141
+ get: (_, /** @type {string} */ file) => fs.statSync(file.replace(/^\/@fs/, '')).size
142
+ }
143
+ ),
126
144
  nodes: manifest_data.nodes.map((node, index) => {
127
145
  return async () => {
128
146
  /** @type {import('types').SSRNode} */
@@ -470,7 +488,10 @@ export async function dev(vite, vite_config, svelte_config) {
470
488
 
471
489
  const server = new Server(manifest);
472
490
 
473
- await server.init({ env });
491
+ await server.init({
492
+ env,
493
+ read: (file) => createReadableStream(file.replace(/^\/@fs/, ''))
494
+ });
474
495
 
475
496
  const request = await getRequest({
476
497
  base,
@@ -505,7 +526,10 @@ export async function dev(vite, vite_config, svelte_config) {
505
526
  if (remoteAddress) return remoteAddress;
506
527
  throw new Error('Could not determine clientAddress');
507
528
  },
508
- read: (file) => fs.readFileSync(path.join(svelte_config.kit.files.assets, file))
529
+ read: (file) => fs.readFileSync(path.join(svelte_config.kit.files.assets, file)),
530
+ before_handle: (event, config, prerender) => {
531
+ async_local_storage.enterWith({ event, config, prerender });
532
+ }
509
533
  });
510
534
 
511
535
  if (rendered.status === 404) {
@@ -1,9 +1,9 @@
1
1
  import path from 'node:path';
2
2
  import { posixify } from '../../../utils/filesystem.js';
3
3
  import { strip_virtual_prefix } from '../utils.js';
4
- import { env_dynamic_private, env_static_private } from '../module_ids.js';
4
+ import { app_server, env_dynamic_private, env_static_private } from '../module_ids.js';
5
5
 
6
- const ILLEGAL_IMPORTS = new Set([env_dynamic_private, env_static_private]);
6
+ const ILLEGAL_IMPORTS = new Set([env_dynamic_private, env_static_private, app_server]);
7
7
  const ILLEGAL_MODULE_NAME_PATTERN = /.*\.server\..+/;
8
8
 
9
9
  /**
@@ -101,5 +101,9 @@ export function normalize_id(id, lib, cwd) {
101
101
  id = path.relative(cwd, id);
102
102
  }
103
103
 
104
+ if (id === app_server) {
105
+ return '$app/server';
106
+ }
107
+
104
108
  return posixify(id);
105
109
  }