filesystem-routing 0.3.1 → 0.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/README.md +49 -2
- package/dist/analyze.d.ts +11 -0
- package/dist/analyze.js +28 -1
- package/dist/api.d.ts +13 -6
- package/dist/api.js +11 -7
- package/dist/convention.d.ts +8 -0
- package/dist/convention.js +14 -7
- package/dist/flags.d.ts +11 -0
- package/dist/flags.js +11 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/manifest.d.ts +13 -0
- package/dist/router.d.ts +8 -1
- package/dist/router.js +47 -19
- package/dist/tree.js +1 -1
- package/dist/vite/constants.d.ts +9 -0
- package/dist/vite/constants.js +9 -0
- package/dist/vite/index.d.ts +1 -1
- package/dist/vite/index.js +39 -17
- package/dist/vite/types.js +18 -9
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -165,7 +165,8 @@ pages get:
|
|
|
165
165
|
|
|
166
166
|
Dispatch ships as fetch-style middleware — `filesystem-routing/api` matches
|
|
167
167
|
requests against the manifest's handler refs and composes into any
|
|
168
|
-
`(request, next)` chain, an SSR handler's middleware option
|
|
168
|
+
`(request, next)` or `(event, next)` chain, an SSR handler's middleware option
|
|
169
|
+
(`@solidjs/vite-plugin`'s `start.middleware`) included:
|
|
169
170
|
|
|
170
171
|
```ts
|
|
171
172
|
// src/middleware.ts
|
|
@@ -175,7 +176,8 @@ import { createAPIHandler } from "filesystem-routing/api";
|
|
|
175
176
|
export default [createAPIHandler(routes)];
|
|
176
177
|
```
|
|
177
178
|
|
|
178
|
-
Handlers receive the
|
|
179
|
+
Handlers receive the event the chain passes in or, in a `(request, next)`
|
|
180
|
+
chain, the request event of the surrounding scope (the storage
|
|
179
181
|
`provideRequestEvent` establishes — pass `getEvent` to dispatch outside
|
|
180
182
|
one), with the matched `params` written onto it. Unmatched requests (no
|
|
181
183
|
route, or no handler for the method) advance the chain; `HEAD` falls back
|
|
@@ -190,6 +192,50 @@ server environment's router only (SolidStart pairs it with `components:
|
|
|
190
192
|
false` in SPA mode, so the server manifest routes requests without shipping
|
|
191
193
|
page modules) — see the per-environment `routers` option below.
|
|
192
194
|
|
|
195
|
+
## Server-function pages
|
|
196
|
+
|
|
197
|
+
With `serverComponents: true`, a page whose default export begins with a
|
|
198
|
+
`"use server"` directive is a server-function page:
|
|
199
|
+
|
|
200
|
+
```tsx
|
|
201
|
+
// src/routes/stories/[id].tsx
|
|
202
|
+
export const route = { live: true }; // any config the emission adapter reads
|
|
203
|
+
|
|
204
|
+
export default async function Story({ params }) {
|
|
205
|
+
"use server";
|
|
206
|
+
const story = await db.stories.get(params.id);
|
|
207
|
+
return props => <article>{story.title}{props.children}</article>;
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
The scanner sees the directive (on an inline function declaration or
|
|
212
|
+
expression, or a top-level function the default export names — not through
|
|
213
|
+
a wrapper call or a re-export) and records two neutral facts on the entry:
|
|
214
|
+
`server: true`, and a `$component` ref marked `eager` and picking only
|
|
215
|
+
`default`. The client side of such a module is a stub, so there is no code
|
|
216
|
+
worth a chunk: the Vite adapter delivers the ref eagerly (`{ src, require }`)
|
|
217
|
+
even with code splitting on, and it is not a build input. What a
|
|
218
|
+
server-rendered page *means* — how the stub is called, cached, and mounted —
|
|
219
|
+
is the emission adapter's business; the scanner only states the fact.
|
|
220
|
+
|
|
221
|
+
Adapters that carry code only server pages need gate it on
|
|
222
|
+
`filesystem-routing/flags`:
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
import { serverRoutes } from "filesystem-routing/flags";
|
|
226
|
+
import * as server from "./server.js"; // dropped from builds with no server page
|
|
227
|
+
|
|
228
|
+
if (serverRoutes && entry.server) server.wrap(entry);
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
The package ships `flags` as a real module whose facts are all `true` — the
|
|
232
|
+
safe answer wherever no plugin hook is in the loop (dev prebundling, a
|
|
233
|
+
hand-built manifest, another bundler). In builds the Vite plugin serves the
|
|
234
|
+
scan's answer instead, folded to a literal, so a gated static import
|
|
235
|
+
tree-shakes out of every app without a server page and is an ordinary
|
|
236
|
+
static import — present at hydration — in every app with one. The option is
|
|
237
|
+
off by default; a manifest built without it is byte-identical to before.
|
|
238
|
+
|
|
193
239
|
## Custom conventions
|
|
194
240
|
|
|
195
241
|
Conventions plug in at two independent seams, so a custom scheme picks the
|
|
@@ -310,6 +356,7 @@ fileRoutes({
|
|
|
310
356
|
| --- | --- |
|
|
311
357
|
| `httpMethods` | emit `$GET`, `$POST`, … refs for uppercase handler exports; a module with handlers but no default export routes without being a page |
|
|
312
358
|
| `components` | set `false` to route without emitting `$component` refs, keeping page modules out of that environment's bundle |
|
|
359
|
+
| `serverComponents` | recognize server-function pages: a `"use server"` default export is flagged `server: true` and delivered eagerly; scan facts are served from `filesystem-routing/flags` (defaults to `false`) |
|
|
313
360
|
| `buildInputs` | environments whose build takes every code-split route module as an entry |
|
|
314
361
|
| `codeSplitting` | set `false` to deliver every module ref as an eager static import — no `lazy()` components, zero dynamic imports, one bundle — for small apps where per-chunk overhead outweighs the split (defaults to `true`; with `@solidjs/router` requires the next release, newer than `2.0.0-next.14`) |
|
|
315
362
|
| `moduleId` | the id the manifest is served from |
|
package/dist/analyze.d.ts
CHANGED
|
@@ -8,6 +8,17 @@ export type { StaticExportEntry };
|
|
|
8
8
|
* a compiler that already performs export analysis can provide it instead.
|
|
9
9
|
*/
|
|
10
10
|
export declare function analyzeModule(src: string): StaticExportEntry[];
|
|
11
|
+
export interface RouteModuleAnalysis {
|
|
12
|
+
exports: StaticExportEntry[];
|
|
13
|
+
/**
|
|
14
|
+
* The directive prologue of the default export, when it is a function
|
|
15
|
+
* declared in this module (inline, or a top-level binding it names):
|
|
16
|
+
* `"use server"` marks a page whose component runs on the server.
|
|
17
|
+
*/
|
|
18
|
+
defaultDirective?: string;
|
|
19
|
+
}
|
|
20
|
+
/** `analyzeModule` plus what the default export's function body declares. */
|
|
21
|
+
export declare function analyzeRouteModule(src: string): RouteModuleAnalysis;
|
|
11
22
|
export declare function getExportName(entry: StaticExportEntry): string;
|
|
12
23
|
/**
|
|
13
24
|
* Returns the export name only when it is backed by a same-named local
|
package/dist/analyze.js
CHANGED
|
@@ -24,6 +24,10 @@ function loadTsrxParser(src) {
|
|
|
24
24
|
* a compiler that already performs export analysis can provide it instead.
|
|
25
25
|
*/
|
|
26
26
|
export function analyzeModule(src) {
|
|
27
|
+
return analyzeRouteModule(src).exports;
|
|
28
|
+
}
|
|
29
|
+
/** `analyzeModule` plus what the default export's function body declares. */
|
|
30
|
+
export function analyzeRouteModule(src) {
|
|
27
31
|
const source = fs.readFileSync(src, "utf-8");
|
|
28
32
|
let result;
|
|
29
33
|
if (src.endsWith(".tsrx")) {
|
|
@@ -44,7 +48,30 @@ export function analyzeModule(src) {
|
|
|
44
48
|
const error = result.errors[0];
|
|
45
49
|
if (error)
|
|
46
50
|
throw new SyntaxError(`Failed to parse ${src}:\n${error.codeframe || error.message}`);
|
|
47
|
-
return
|
|
51
|
+
return {
|
|
52
|
+
exports: result.module.staticExports.flatMap(({ entries }) => entries.filter(entry => !entry.isType && entry.exportName.kind !== "None")),
|
|
53
|
+
defaultDirective: defaultDirectiveOf(result.program.body)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function defaultDirectiveOf(body) {
|
|
57
|
+
let fn = body.find(node => node.type === "ExportDefaultDeclaration")?.declaration;
|
|
58
|
+
if (fn?.type === "Identifier") {
|
|
59
|
+
// `export default Story` — follow the name to a top-level function.
|
|
60
|
+
const name = fn.name;
|
|
61
|
+
fn = undefined;
|
|
62
|
+
for (const node of body) {
|
|
63
|
+
if (node.type === "FunctionDeclaration" && node.id?.name === name)
|
|
64
|
+
fn = node;
|
|
65
|
+
else if (node.type === "VariableDeclaration")
|
|
66
|
+
for (const d of node.declarations)
|
|
67
|
+
if (d.id?.name === name)
|
|
68
|
+
fn = d.init;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (!fn || !/Function(Declaration|Expression)$/.test(fn.type))
|
|
72
|
+
return;
|
|
73
|
+
const first = fn.body?.body?.[0];
|
|
74
|
+
return first?.type === "ExpressionStatement" ? first.directive : undefined;
|
|
48
75
|
}
|
|
49
76
|
export function getExportName(entry) {
|
|
50
77
|
return entry.exportName.name ?? "default";
|
package/dist/api.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The API dispatch adapter: matches requests against the flat manifest's
|
|
3
3
|
* handler refs (`$GET`, `$POST`, …) and serves them as fetch-style
|
|
4
|
-
* middleware, so API routes compose into any `(request, next)`
|
|
5
|
-
* SSR handler's middleware option, a framework's
|
|
6
|
-
* bundler or server involvement.
|
|
4
|
+
* middleware, so API routes compose into any `(request, next)` or
|
|
5
|
+
* `(event, next)` chain — an SSR handler's middleware option, a framework's
|
|
6
|
+
* request pipeline — with no bundler or server involvement.
|
|
7
7
|
*
|
|
8
8
|
* Ported from SolidStart's `server/routes.ts` (the radix3 matcher) and the
|
|
9
9
|
* method dispatch in its `server/handler.ts`.
|
|
@@ -69,7 +69,8 @@ export interface APIHandlerOptions {
|
|
|
69
69
|
base?: string;
|
|
70
70
|
/**
|
|
71
71
|
* How the adapter reaches the request event it hands to handlers. Defaults
|
|
72
|
-
* to the
|
|
72
|
+
* to the event the chain passes in or, when it passes a bare `Request`, to
|
|
73
|
+
* the core request-event scope (the storage `provideRequestEvent`
|
|
73
74
|
* establishes), which is where a middleware chain runs — override it when
|
|
74
75
|
* dispatching outside one, or to hand handlers a richer event.
|
|
75
76
|
*/
|
|
@@ -85,12 +86,18 @@ export interface APIHandlerOptions {
|
|
|
85
86
|
* export default [createAPIHandler(routes)];
|
|
86
87
|
* ```
|
|
87
88
|
*
|
|
89
|
+
* The first argument is the request or a request event carrying it
|
|
90
|
+
* (`event.request`), so the handler fits `(request, next)` and
|
|
91
|
+
* `(event, next)` chains alike; it calls `next()` without arguments.
|
|
92
|
+
*
|
|
88
93
|
* Dispatch preserves SolidStart's semantics: unmatched requests (no route,
|
|
89
94
|
* or no handler for the method) advance the chain; `HEAD` falls back to the
|
|
90
95
|
* `GET` handler; matched params are written to `event.params` before the
|
|
91
96
|
* handler runs. A handler returning `undefined` declines the request —
|
|
92
|
-
* allowed for `GET` only (anything else throws): a page module falls
|
|
97
|
+
* allowed for `GET` and `HEAD` only (anything else throws): a page module falls
|
|
93
98
|
* through to the chain (its component renders instead), a handler-only
|
|
94
99
|
* module answers 404.
|
|
95
100
|
*/
|
|
96
|
-
export declare function createAPIHandler(routes: readonly FileRouteHandlers[], options?: APIHandlerOptions): (
|
|
101
|
+
export declare function createAPIHandler(routes: readonly FileRouteHandlers[], options?: APIHandlerOptions): (input: Request | {
|
|
102
|
+
request: Request;
|
|
103
|
+
}, next: (request?: Request) => Response | Promise<Response>) => Promise<Response>;
|
package/dist/api.js
CHANGED
|
@@ -62,8 +62,8 @@ const RequestContext = Symbol.for("solid.RequestContext");
|
|
|
62
62
|
function getScopedEvent() {
|
|
63
63
|
const event = globalThis[RequestContext]?.getStore();
|
|
64
64
|
if (!event) {
|
|
65
|
-
throw new Error("API route dispatched outside a request-event scope.
|
|
66
|
-
"
|
|
65
|
+
throw new Error("API route dispatched outside a request-event scope. Pass the request event " +
|
|
66
|
+
"instead of the request, compose the handler inside `provideRequestEvent`, " +
|
|
67
67
|
"or pass `getEvent`.");
|
|
68
68
|
}
|
|
69
69
|
return event;
|
|
@@ -85,18 +85,22 @@ function toResponse(result) {
|
|
|
85
85
|
* export default [createAPIHandler(routes)];
|
|
86
86
|
* ```
|
|
87
87
|
*
|
|
88
|
+
* The first argument is the request or a request event carrying it
|
|
89
|
+
* (`event.request`), so the handler fits `(request, next)` and
|
|
90
|
+
* `(event, next)` chains alike; it calls `next()` without arguments.
|
|
91
|
+
*
|
|
88
92
|
* Dispatch preserves SolidStart's semantics: unmatched requests (no route,
|
|
89
93
|
* or no handler for the method) advance the chain; `HEAD` falls back to the
|
|
90
94
|
* `GET` handler; matched params are written to `event.params` before the
|
|
91
95
|
* handler runs. A handler returning `undefined` declines the request —
|
|
92
|
-
* allowed for `GET` only (anything else throws): a page module falls
|
|
96
|
+
* allowed for `GET` and `HEAD` only (anything else throws): a page module falls
|
|
93
97
|
* through to the chain (its component renders instead), a handler-only
|
|
94
98
|
* module answers 404.
|
|
95
99
|
*/
|
|
96
100
|
export function createAPIHandler(routes, options = {}) {
|
|
97
101
|
const match = createAPIMatcher(routes);
|
|
98
|
-
|
|
99
|
-
|
|
102
|
+
return async (input, next) => {
|
|
103
|
+
const request = "request" in input ? input.request : input;
|
|
100
104
|
const url = new URL(request.url);
|
|
101
105
|
const matched = match(stripPathBase(url.pathname, options.base ?? "/"), request.method);
|
|
102
106
|
if (!matched)
|
|
@@ -105,12 +109,12 @@ export function createAPIHandler(routes, options = {}) {
|
|
|
105
109
|
const fn = request.method === "HEAD" ? mod.HEAD || mod.GET : mod[request.method];
|
|
106
110
|
if (typeof fn !== "function")
|
|
107
111
|
return next();
|
|
108
|
-
const event = getEvent();
|
|
112
|
+
const event = options.getEvent?.() ?? ("request" in input ? input : getScopedEvent());
|
|
109
113
|
event.params = matched.params || {};
|
|
110
114
|
const result = await fn(event);
|
|
111
115
|
if (result !== undefined)
|
|
112
116
|
return toResponse(result);
|
|
113
|
-
if (request.method !== "GET") {
|
|
117
|
+
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
114
118
|
throw new Error(`API handler for ${request.method} "${request.url}" did not return a response.`);
|
|
115
119
|
}
|
|
116
120
|
if (!matched.isPage)
|
package/dist/convention.d.ts
CHANGED
|
@@ -30,6 +30,14 @@ export interface PageFileSystemRouterConfig extends FileSystemRouterConfig {
|
|
|
30
30
|
* Pass `true` for the standard set. Defaults to `false`.
|
|
31
31
|
*/
|
|
32
32
|
httpMethods?: boolean | readonly string[];
|
|
33
|
+
/**
|
|
34
|
+
* Recognize server-function pages: a default export whose body begins with
|
|
35
|
+
* a `"use server"` directive is flagged `server: true` and its `$component`
|
|
36
|
+
* delivered eagerly — the client side of such a module is a stub, not a
|
|
37
|
+
* chunk's worth of component. What a server-rendered page means is the
|
|
38
|
+
* emission adapter's business. Defaults to `false`.
|
|
39
|
+
*/
|
|
40
|
+
serverComponents?: boolean;
|
|
33
41
|
}
|
|
34
42
|
/**
|
|
35
43
|
* The module convention proven by SolidStart: a route module is a page when
|
package/dist/convention.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { analyzeRouteModule, getExportName, getLocalExportName } from "./analyze.js";
|
|
2
2
|
import { BaseFileSystemRouter, cleanPath } from "./router.js";
|
|
3
3
|
/**
|
|
4
4
|
* The filename convention proven by SolidStart:
|
|
@@ -85,24 +85,31 @@ export class PageFileSystemRouter extends BaseFileSystemRouter {
|
|
|
85
85
|
};
|
|
86
86
|
}
|
|
87
87
|
const methods = httpMethodsOf(this.config);
|
|
88
|
-
const exports =
|
|
88
|
+
const { exports, defaultDirective } = analyzeRouteModule(src);
|
|
89
89
|
const exportNames = exports.map(getExportName);
|
|
90
90
|
const localExportNames = exports.map(getLocalExportName).filter(name => name !== undefined);
|
|
91
91
|
const hasDefault = exportNames.includes("default");
|
|
92
92
|
const hasRouteConfig = exportNames.includes("route");
|
|
93
93
|
const handlers = createHandlerRefs(src, exportNames, methods);
|
|
94
94
|
const hasHandlers = Object.keys(handlers).length > 0;
|
|
95
|
+
// A server-function page (opt-in): the client gets a stub, not a chunk's
|
|
96
|
+
// worth of component, so it is delivered eagerly and flagged for the adapter.
|
|
97
|
+
const server = this.config.serverComponents === true && hasDefault && defaultDirective === "use server";
|
|
95
98
|
if (hasDefault || hasHandlers) {
|
|
96
99
|
return {
|
|
97
100
|
page: hasDefault,
|
|
101
|
+
...(server ? { server } : {}),
|
|
98
102
|
$component: components && hasDefault
|
|
99
103
|
? {
|
|
100
104
|
src: src,
|
|
101
|
-
pick:
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
pick: server
|
|
106
|
+
? ["default"]
|
|
107
|
+
: [
|
|
108
|
+
...localExportNames.filter(name => name !== "route" && !methods.includes(name)),
|
|
109
|
+
"default",
|
|
110
|
+
"$css"
|
|
111
|
+
],
|
|
112
|
+
...(server ? { eager: true } : {})
|
|
106
113
|
}
|
|
107
114
|
: undefined,
|
|
108
115
|
$$route: hasRouteConfig
|
package/dist/flags.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Facts about the route scan, for emission adapters to gate static imports
|
|
3
|
+
* on. This file is the fallback: every fact is `true`, so code that reads it
|
|
4
|
+
* without the Vite plugin in the loop (dev prebundling, a hand-built
|
|
5
|
+
* manifest, another bundler) errs toward "present", never toward "broken".
|
|
6
|
+
* `filesystem-routing/vite` replaces this module in builds with the scan's
|
|
7
|
+
* actual answers, folded to literals, so an adapter's
|
|
8
|
+
* `if (serverRoutes) use(server)` drops its import when no route needs it.
|
|
9
|
+
*/
|
|
10
|
+
/** `true` when some page's component is a server function (`server: true`). */
|
|
11
|
+
export declare const serverRoutes: boolean;
|
package/dist/flags.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Facts about the route scan, for emission adapters to gate static imports
|
|
3
|
+
* on. This file is the fallback: every fact is `true`, so code that reads it
|
|
4
|
+
* without the Vite plugin in the loop (dev prebundling, a hand-built
|
|
5
|
+
* manifest, another bundler) errs toward "present", never toward "broken".
|
|
6
|
+
* `filesystem-routing/vite` replaces this module in builds with the scan's
|
|
7
|
+
* actual answers, folded to literals, so an adapter's
|
|
8
|
+
* `if (serverRoutes) use(server)` drops its import when no route needs it.
|
|
9
|
+
*/
|
|
10
|
+
/** `true` when some page's component is a server function (`server: true`). */
|
|
11
|
+
export const serverRoutes = true;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { analyzeModule, getExportName, getLocalExportName } from "./analyze.ts";
|
|
2
|
-
export type { StaticExportEntry } from "./analyze.ts";
|
|
1
|
+
export { analyzeModule, analyzeRouteModule, getExportName, getLocalExportName } from "./analyze.ts";
|
|
2
|
+
export type { RouteModuleAnalysis, StaticExportEntry } from "./analyze.ts";
|
|
3
3
|
export type { ModuleRef, RouteManifestEntry } from "./manifest.ts";
|
|
4
4
|
export { BaseFileSystemRouter, cleanPath, glob, normalizePath, type FileSystemRouterConfig } from "./router.ts";
|
|
5
5
|
export { HTTP_METHODS, PageFileSystemRouter, routePathFromFile, type PageFileSystemRouterConfig } from "./convention.ts";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { analyzeModule, getExportName, getLocalExportName } from "./analyze.js";
|
|
1
|
+
export { analyzeModule, analyzeRouteModule, getExportName, getLocalExportName } from "./analyze.js";
|
|
2
2
|
export { BaseFileSystemRouter, cleanPath, glob, normalizePath } from "./router.js";
|
|
3
3
|
export { HTTP_METHODS, PageFileSystemRouter, routePathFromFile } from "./convention.js";
|
|
4
4
|
export { FlatFileSystemRouter, flatRoutePathFromFile } from "./flat.js";
|
package/dist/manifest.d.ts
CHANGED
|
@@ -27,6 +27,12 @@ export interface ModuleRef {
|
|
|
27
27
|
src: string;
|
|
28
28
|
/** The named exports (or `"default"`) this ref selects from the module. */
|
|
29
29
|
pick: string[];
|
|
30
|
+
/**
|
|
31
|
+
* Deliver eagerly even under code splitting. Set by a convention when the
|
|
32
|
+
* module has no client code worth a chunk — a server-function page, whose
|
|
33
|
+
* client side is a stub — so the ref is materialized `{ src, require }`.
|
|
34
|
+
*/
|
|
35
|
+
eager?: boolean;
|
|
30
36
|
}
|
|
31
37
|
export interface RouteManifestEntry {
|
|
32
38
|
/**
|
|
@@ -38,6 +44,13 @@ export interface RouteManifestEntry {
|
|
|
38
44
|
path: string;
|
|
39
45
|
/** `true` when the module renders a page (has a default export). */
|
|
40
46
|
page?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* `true` when the page component is a server function — its default export
|
|
49
|
+
* begins with a `"use server"` directive. The convention delivers such a
|
|
50
|
+
* `$component` eagerly; what a server-rendered page means is the emission
|
|
51
|
+
* adapter's business.
|
|
52
|
+
*/
|
|
53
|
+
server?: boolean;
|
|
41
54
|
/** Lazy ref to the page component module. */
|
|
42
55
|
$component?: ModuleRef;
|
|
43
56
|
/** Eager ref to the route config (`route` export), when present. */
|
package/dist/router.d.ts
CHANGED
|
@@ -38,14 +38,21 @@ export declare function cleanPath(src: string, config: FileSystemRouterConfig):
|
|
|
38
38
|
*/
|
|
39
39
|
export declare class BaseFileSystemRouter extends EventTarget {
|
|
40
40
|
routes: RouteManifestEntry[];
|
|
41
|
+
/** Source file that first produced each route path, so incremental changes keep scan order. */
|
|
42
|
+
private sources;
|
|
41
43
|
config: FileSystemRouterConfig;
|
|
44
|
+
/**
|
|
45
|
+
* Route path each source file produced. A removed file can no longer be
|
|
46
|
+
* passed to `toRoute`, and `toPath` may give another file's route.
|
|
47
|
+
*/
|
|
48
|
+
private routePaths;
|
|
42
49
|
constructor(config: FileSystemRouterConfig);
|
|
43
50
|
glob(): string;
|
|
44
51
|
buildRoutes(): Promise<RouteManifestEntry[]>;
|
|
45
52
|
isRoute(src: string): boolean;
|
|
46
53
|
toPath(src: string): string | undefined;
|
|
47
54
|
toRoute(src: string): RouteManifestEntry | undefined;
|
|
48
|
-
_addRoute(route: RouteManifestEntry): boolean;
|
|
55
|
+
_addRoute(route: RouteManifestEntry, src?: string): boolean;
|
|
49
56
|
addRoute(src: string): Promise<void>;
|
|
50
57
|
reload(route: string, type: "update" | "remove" | "add"): void;
|
|
51
58
|
updateRoute(src_: string): Promise<void>;
|
package/dist/router.js
CHANGED
|
@@ -20,7 +20,14 @@ export function cleanPath(src, config) {
|
|
|
20
20
|
*/
|
|
21
21
|
export class BaseFileSystemRouter extends EventTarget {
|
|
22
22
|
routes;
|
|
23
|
+
/** Source file that first produced each route path, so incremental changes keep scan order. */
|
|
24
|
+
sources = new Map();
|
|
23
25
|
config;
|
|
26
|
+
/**
|
|
27
|
+
* Route path each source file produced. A removed file can no longer be
|
|
28
|
+
* passed to `toRoute`, and `toPath` may give another file's route.
|
|
29
|
+
*/
|
|
30
|
+
routePaths = new Map();
|
|
24
31
|
constructor(config) {
|
|
25
32
|
super();
|
|
26
33
|
this.routes = [];
|
|
@@ -36,7 +43,7 @@ export class BaseFileSystemRouter extends EventTarget {
|
|
|
36
43
|
return posix.join(fg.convertPathToPattern(this.config.dir), "**/*") + suffix;
|
|
37
44
|
}
|
|
38
45
|
async buildRoutes() {
|
|
39
|
-
for (const src of glob(this.glob())) {
|
|
46
|
+
for (const src of glob(this.glob()).sort()) {
|
|
40
47
|
await this.addRoute(src);
|
|
41
48
|
}
|
|
42
49
|
return this.routes;
|
|
@@ -54,12 +61,28 @@ export class BaseFileSystemRouter extends EventTarget {
|
|
|
54
61
|
return this.config.toRoute(src, this);
|
|
55
62
|
throw new Error("Not implemented");
|
|
56
63
|
}
|
|
57
|
-
_addRoute(route) {
|
|
64
|
+
_addRoute(route, src) {
|
|
58
65
|
const idx = this.routes.findIndex(r => r.path === route.path);
|
|
59
|
-
if (idx >= 0)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
if (idx >= 0) {
|
|
67
|
+
// Replace in place: an edited file keeps its position. The source that
|
|
68
|
+
// first produced the path still decides it, as in a full scan.
|
|
69
|
+
this.routes[idx] = route;
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
if (src !== undefined)
|
|
73
|
+
this.sources.set(route.path, src);
|
|
74
|
+
// A new file goes where a full scan would put it.
|
|
75
|
+
const at = src === undefined
|
|
76
|
+
? -1
|
|
77
|
+
: this.routes.findIndex(r => {
|
|
78
|
+
const other = this.sources.get(r.path);
|
|
79
|
+
return other !== undefined && other > src;
|
|
80
|
+
});
|
|
81
|
+
if (at === -1)
|
|
82
|
+
this.routes.push(route);
|
|
83
|
+
else
|
|
84
|
+
this.routes.splice(at, 0, route);
|
|
85
|
+
return false;
|
|
63
86
|
}
|
|
64
87
|
async addRoute(src) {
|
|
65
88
|
src = normalizePath(src);
|
|
@@ -67,7 +90,10 @@ export class BaseFileSystemRouter extends EventTarget {
|
|
|
67
90
|
try {
|
|
68
91
|
const route = this.toRoute(src);
|
|
69
92
|
if (route) {
|
|
70
|
-
this.
|
|
93
|
+
if (this.routePaths.get(src) !== route.path)
|
|
94
|
+
this.removeRoute(src);
|
|
95
|
+
this._addRoute(route, src);
|
|
96
|
+
this.routePaths.set(src, route.path);
|
|
71
97
|
this.reload(route.path, "add");
|
|
72
98
|
}
|
|
73
99
|
}
|
|
@@ -90,7 +116,10 @@ export class BaseFileSystemRouter extends EventTarget {
|
|
|
90
116
|
try {
|
|
91
117
|
const route = this.toRoute(src);
|
|
92
118
|
if (route) {
|
|
93
|
-
|
|
119
|
+
if (this.routePaths.get(src) !== route.path)
|
|
120
|
+
this.removeRoute(src_);
|
|
121
|
+
const updated = this._addRoute(route, src);
|
|
122
|
+
this.routePaths.set(src, route.path);
|
|
94
123
|
this.reload(route.path, updated ? "update" : "add");
|
|
95
124
|
}
|
|
96
125
|
else {
|
|
@@ -104,17 +133,16 @@ export class BaseFileSystemRouter extends EventTarget {
|
|
|
104
133
|
}
|
|
105
134
|
removeRoute(src) {
|
|
106
135
|
src = normalizePath(src);
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
136
|
+
const path = this.routePaths.get(src);
|
|
137
|
+
if (path === undefined)
|
|
138
|
+
return;
|
|
139
|
+
this.routePaths.delete(src);
|
|
140
|
+
const idx = this.routes.findIndex(r => r.path === path);
|
|
141
|
+
if (idx === -1)
|
|
142
|
+
return;
|
|
143
|
+
this.routes.splice(idx, 1);
|
|
144
|
+
this.sources.delete(path);
|
|
145
|
+
this.reload(path, "remove");
|
|
118
146
|
}
|
|
119
147
|
on(type, cb) {
|
|
120
148
|
this.addEventListener(type, cb);
|
package/dist/tree.js
CHANGED
|
@@ -21,7 +21,7 @@ export function buildRouteTree(entries) {
|
|
|
21
21
|
processRoute(parentRoute.children || (parentRoute.children = []), route, id.slice(parentRoute.id.length));
|
|
22
22
|
}
|
|
23
23
|
return [...entries]
|
|
24
|
-
.sort((a, b) => a.path.length - b.path.length)
|
|
24
|
+
.sort((a, b) => (a.path.length - b.path.length) || a.path.localeCompare(b.path))
|
|
25
25
|
.reduce((routes, route) => {
|
|
26
26
|
processRoute(routes, route, route.path);
|
|
27
27
|
return routes;
|
package/dist/vite/constants.d.ts
CHANGED
|
@@ -6,3 +6,12 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export declare const moduleId = "virtual:file-routes";
|
|
8
8
|
export declare const DEFAULT_EXTENSIONS: string[];
|
|
9
|
+
/**
|
|
10
|
+
* The module adapters import scan facts from — `filesystem-routing/flags`.
|
|
11
|
+
* The package ships it as a real file whose facts are all `true` (the safe
|
|
12
|
+
* answer wherever no plugin hook is in the loop: dev prebundling, a custom
|
|
13
|
+
* manifest, another bundler); the Vite plugin serves the scan's answer in
|
|
14
|
+
* builds so gated code folds away.
|
|
15
|
+
*/
|
|
16
|
+
export declare const flagsId = "filesystem-routing/flags";
|
|
17
|
+
export declare const resolvedFlagsId: string;
|
package/dist/vite/constants.js
CHANGED
|
@@ -6,3 +6,12 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export const moduleId = "virtual:file-routes";
|
|
8
8
|
export const DEFAULT_EXTENSIONS = ["js", "jsx", "ts", "tsx", "tsrx"];
|
|
9
|
+
/**
|
|
10
|
+
* The module adapters import scan facts from — `filesystem-routing/flags`.
|
|
11
|
+
* The package ships it as a real file whose facts are all `true` (the safe
|
|
12
|
+
* answer wherever no plugin hook is in the loop: dev prebundling, a custom
|
|
13
|
+
* manifest, another bundler); the Vite plugin serves the scan's answer in
|
|
14
|
+
* builds so gated code folds away.
|
|
15
|
+
*/
|
|
16
|
+
export const flagsId = "filesystem-routing/flags";
|
|
17
|
+
export const resolvedFlagsId = "\0" + flagsId;
|
package/dist/vite/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { DEFAULT_EXTENSIONS, moduleId } from "./constants.ts";
|
|
|
5
5
|
export { DEFAULT_EXTENSIONS, moduleId };
|
|
6
6
|
export { sanitizeChunkFileName, toPickId, treeShake } from "./tree-shake.ts";
|
|
7
7
|
export { fileSystemWatcher } from "./fs-watcher.ts";
|
|
8
|
-
export interface FileRoutesOptions extends Pick<PageFileSystemRouterConfig, "components" | "httpMethods" | "toPath" | "toRoute"> {
|
|
8
|
+
export interface FileRoutesOptions extends Pick<PageFileSystemRouterConfig, "components" | "httpMethods" | "serverComponents" | "toPath" | "toRoute"> {
|
|
9
9
|
/** Route directory, relative to the Vite root. Defaults to `src/routes`. */
|
|
10
10
|
dir?: string;
|
|
11
11
|
/** File extensions that participate in routing. Defaults to js/jsx/ts/tsx. */
|
package/dist/vite/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { dirname, relative, resolve } from "node:path";
|
|
|
3
3
|
import { PageFileSystemRouter } from "../convention.js";
|
|
4
4
|
import { normalizePath } from "../router.js";
|
|
5
5
|
import { buildRouteTree } from "../tree.js";
|
|
6
|
-
import { DEFAULT_EXTENSIONS, moduleId } from "./constants.js";
|
|
6
|
+
import { DEFAULT_EXTENSIONS, flagsId, moduleId, resolvedFlagsId } from "./constants.js";
|
|
7
7
|
import { fileSystemWatcher } from "./fs-watcher.js";
|
|
8
8
|
import { sanitizeChunkFileName, toPickId, treeShake } from "./tree-shake.js";
|
|
9
9
|
import { serializeTypes } from "./types.js";
|
|
@@ -21,6 +21,15 @@ const HANDLER_REF = /^\$[A-Z]+$/;
|
|
|
21
21
|
* own default: the `client` environment is the one client consumer.
|
|
22
22
|
*/
|
|
23
23
|
const isServerConsumer = (name, consumer) => (consumer ?? (name === "client" ? "client" : "server")) === "server";
|
|
24
|
+
/**
|
|
25
|
+
* The id a route module ref is loaded from: its source plus its picks. On the
|
|
26
|
+
* server a server page's module is loaded whole, because the server-function
|
|
27
|
+
* handler imports it whole too, and a second (picked) instance would register
|
|
28
|
+
* the page's server function twice.
|
|
29
|
+
*/
|
|
30
|
+
const toModuleId = (route, ref, serverConsumer) => serverConsumer && route.server === true && ref.src === route.$component?.src
|
|
31
|
+
? ref.src
|
|
32
|
+
: toPickId(ref.src, ref.pick);
|
|
24
33
|
/**
|
|
25
34
|
* The client's view of a shared server manifest: handler refs removed. A
|
|
26
35
|
* client bundle can never invoke a request handler, but serializing its ref
|
|
@@ -106,8 +115,6 @@ export function fileRoutes(options = {}) {
|
|
|
106
115
|
mkdirSync(dirname(file), { recursive: true });
|
|
107
116
|
writeFileSync(file, contents);
|
|
108
117
|
}
|
|
109
|
-
/** The id a route module ref is loaded from: its source plus its picks. */
|
|
110
|
-
const toModuleId = (ref) => toPickId(ref.src, ref.pick);
|
|
111
118
|
return [
|
|
112
119
|
{
|
|
113
120
|
name: "filesystem-routing",
|
|
@@ -138,6 +145,7 @@ export function fileRoutes(options = {}) {
|
|
|
138
145
|
extensions: options.extensions ?? DEFAULT_EXTENSIONS,
|
|
139
146
|
components: options.components,
|
|
140
147
|
httpMethods: options.httpMethods,
|
|
148
|
+
serverComponents: options.serverComponents,
|
|
141
149
|
toPath: options.toPath,
|
|
142
150
|
toRoute: options.toRoute
|
|
143
151
|
});
|
|
@@ -160,7 +168,8 @@ export function fileRoutes(options = {}) {
|
|
|
160
168
|
// A client environment on the shared router never serializes
|
|
161
169
|
// handler refs (see load), so their modules must not become its
|
|
162
170
|
// build entries either.
|
|
163
|
-
const
|
|
171
|
+
const serverConsumer = isServerConsumer(name, _config.consumer);
|
|
172
|
+
const routes = options.routers?.[name] || serverConsumer
|
|
164
173
|
? await router.getRoutes()
|
|
165
174
|
: stripHandlerRefs(await router.getRoutes());
|
|
166
175
|
// Every code-split route module is an entry of its own, so the
|
|
@@ -169,8 +178,8 @@ export function fileRoutes(options = {}) {
|
|
|
169
178
|
const input = [];
|
|
170
179
|
for (const route of routes) {
|
|
171
180
|
for (const [key, ref] of Object.entries(route)) {
|
|
172
|
-
if (ref && key.startsWith("$") && !key.startsWith("$$")) {
|
|
173
|
-
input.push(toModuleId(ref));
|
|
181
|
+
if (ref && key.startsWith("$") && !key.startsWith("$$") && !ref.eager) {
|
|
182
|
+
input.push(toModuleId(route, ref, serverConsumer));
|
|
174
183
|
}
|
|
175
184
|
}
|
|
176
185
|
}
|
|
@@ -181,8 +190,20 @@ export function fileRoutes(options = {}) {
|
|
|
181
190
|
resolveId(source) {
|
|
182
191
|
if (source === virtualId)
|
|
183
192
|
return virtualId;
|
|
193
|
+
if (source === flagsId)
|
|
194
|
+
return resolvedFlagsId;
|
|
184
195
|
},
|
|
185
196
|
async load(loadedId) {
|
|
197
|
+
if (loadedId === resolvedFlagsId) {
|
|
198
|
+
// Facts about the scan, folded to literals so an adapter's static
|
|
199
|
+
// imports gated on them tree-shake out of a build that does not
|
|
200
|
+
// need them. In serve, always `true`: dev has no size to protect
|
|
201
|
+
// and the first server page must work without a restart.
|
|
202
|
+
const router = getRouter(this.environment.name);
|
|
203
|
+
const routes = (router && (await router.getRoutes())) ?? [];
|
|
204
|
+
const serverRoutes = this.environment.mode !== "build" || routes.some(route => route.server === true);
|
|
205
|
+
return `export const serverRoutes = ${serverRoutes};`;
|
|
206
|
+
}
|
|
186
207
|
if (loadedId !== virtualId)
|
|
187
208
|
return;
|
|
188
209
|
const root = this.environment.config.root;
|
|
@@ -191,19 +212,19 @@ export function fileRoutes(options = {}) {
|
|
|
191
212
|
const environmentName = this.environment.name;
|
|
192
213
|
const router = getRouter(environmentName);
|
|
193
214
|
let routes = (router ? await router.getRoutes() : []) ?? [];
|
|
215
|
+
const serverConsumer = isServerConsumer(environmentName, this.environment.config.consumer);
|
|
194
216
|
// The shared router serves every environment, so the split is this
|
|
195
217
|
// adapter's job: client consumers get the manifest without handler
|
|
196
218
|
// refs. An explicit per-environment router already IS the split —
|
|
197
219
|
// serve whatever it emits.
|
|
198
|
-
if (!options.routers?.[environmentName] &&
|
|
199
|
-
!isServerConsumer(environmentName, this.environment.config.consumer)) {
|
|
220
|
+
if (!options.routers?.[environmentName] && !serverConsumer) {
|
|
200
221
|
routes = stripHandlerRefs(routes);
|
|
201
222
|
}
|
|
202
223
|
const serializeEntry = (entry) => JSON.stringify(entry, (key, value) => {
|
|
203
224
|
if (value === undefined)
|
|
204
225
|
return undefined;
|
|
205
226
|
if (key.startsWith("$$")) {
|
|
206
|
-
const buildId = toModuleId(value);
|
|
227
|
+
const buildId = toModuleId(entry, value, serverConsumer);
|
|
207
228
|
const refs = {};
|
|
208
229
|
for (const pick of value.pick) {
|
|
209
230
|
refs[pick] = js.addNamedImport(pick, buildId);
|
|
@@ -215,19 +236,20 @@ export function fileRoutes(options = {}) {
|
|
|
215
236
|
};
|
|
216
237
|
}
|
|
217
238
|
else if (key.startsWith("$")) {
|
|
218
|
-
const buildId = toModuleId(value);
|
|
219
|
-
// With code splitting off
|
|
220
|
-
//
|
|
221
|
-
//
|
|
222
|
-
//
|
|
223
|
-
|
|
239
|
+
const buildId = toModuleId(entry, value, serverConsumer);
|
|
240
|
+
// With code splitting off — or a ref the convention marked
|
|
241
|
+
// `eager` — the ref is delivered eagerly: a namespace import
|
|
242
|
+
// (named imports would fail on the synthetic `$css` pick)
|
|
243
|
+
// behind the `require()` shape eager refs carry, plus the
|
|
244
|
+
// `src` lazy refs already expose.
|
|
245
|
+
if (!codeSplitting || value.eager) {
|
|
224
246
|
return {
|
|
225
|
-
src: relative(root, buildId),
|
|
247
|
+
src: normalizePath(relative(root, buildId)),
|
|
226
248
|
require: `_$() => (${js.addNamespaceImport(buildId)})$_`
|
|
227
249
|
};
|
|
228
250
|
}
|
|
229
251
|
return {
|
|
230
|
-
src: relative(root, buildId),
|
|
252
|
+
src: normalizePath(relative(root, buildId)),
|
|
231
253
|
build: isBuild ? `_$() => import('${buildId}')$_` : undefined,
|
|
232
254
|
import: `_$() => import('${buildId}')$_`
|
|
233
255
|
};
|
package/dist/vite/types.js
CHANGED
|
@@ -32,6 +32,8 @@ const REF_TYPES = ` /** A code-split ref: the delivery adapter emits it as a dy
|
|
|
32
32
|
export interface FileRouteEntry {
|
|
33
33
|
path: string;
|
|
34
34
|
page?: boolean;
|
|
35
|
+
/** The page component is a server function; its \`$component\` is delivered eagerly. */
|
|
36
|
+
server?: boolean;
|
|
35
37
|
$component?: FileRouteLazyRef<any> | FileRouteEagerRef<any>;
|
|
36
38
|
$$route?: FileRouteEagerRef<any>;
|
|
37
39
|
[key: string]: unknown;
|
|
@@ -45,19 +47,22 @@ const REF_TYPES = ` /** A code-split ref: the delivery adapter emits it as a dy
|
|
|
45
47
|
/** Extensions TypeScript can resolve through a `typeof import(...)`. */
|
|
46
48
|
const TYPED_MODULE = /\.([mc]?[jt]sx?)$/;
|
|
47
49
|
/**
|
|
48
|
-
* Types a ref by
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* TypeScript cannot resolve (`.md`, `.mdx`) fall back to the generic ref.
|
|
50
|
+
* Types a ref by its picked exports, keeping the module's `default` and
|
|
51
|
+
* `route` types so an emission adapter can read either without a cast.
|
|
52
|
+
* Modules TypeScript cannot resolve (`.md`, `.mdx`) fall back to the generic ref.
|
|
52
53
|
*/
|
|
53
54
|
function refType(ref, kind, from) {
|
|
54
55
|
const name = kind === "eager" ? "FileRouteEagerRef" : "FileRouteLazyRef";
|
|
55
56
|
if (!TYPED_MODULE.test(ref.src))
|
|
56
57
|
return name;
|
|
58
|
+
const picks = ref.pick.filter(pick => pick !== "$css").map(pick => JSON.stringify(pick));
|
|
59
|
+
if (!picks.length)
|
|
60
|
+
return name;
|
|
57
61
|
let specifier = normalizePath(relative(from, ref.src)).replace(TYPED_MODULE, "");
|
|
58
62
|
if (!specifier.startsWith("."))
|
|
59
63
|
specifier = `./${specifier}`;
|
|
60
|
-
|
|
64
|
+
const moduleType = `typeof import(${JSON.stringify(specifier)})`;
|
|
65
|
+
return `${name}<Pick<${moduleType}, Extract<${picks.join(" | ")}, keyof ${moduleType}>>>`;
|
|
61
66
|
}
|
|
62
67
|
const isLazyRefKey = (key) => key.startsWith("$") && !key.startsWith("$$");
|
|
63
68
|
const isEagerRefKey = (key) => key.startsWith("$$");
|
|
@@ -72,14 +77,18 @@ function entryType(entry, indent, nested, from, codeSplitting) {
|
|
|
72
77
|
if (nested)
|
|
73
78
|
fields.push(`id: ${JSON.stringify(entry.id)}`);
|
|
74
79
|
fields.push(`page: ${entry.page ? "true" : "false"}`);
|
|
80
|
+
if (entry.server)
|
|
81
|
+
fields.push(`server: true`);
|
|
75
82
|
// A convention records refs it did not emit as present-but-undefined keys,
|
|
76
83
|
// so go by the value, not the key.
|
|
77
84
|
const refKeys = Object.keys(entry).filter(key => key.startsWith("$") && entry[key] !== undefined);
|
|
78
85
|
for (const key of refKeys) {
|
|
79
|
-
// With code splitting off, the
|
|
80
|
-
// eagerly, so the declaration
|
|
81
|
-
|
|
82
|
-
|
|
86
|
+
// With code splitting off, or a ref the convention marked `eager`, the
|
|
87
|
+
// delivery adapter materializes lazy refs eagerly, so the declaration
|
|
88
|
+
// describes them as delivered.
|
|
89
|
+
const ref = entry[key];
|
|
90
|
+
const kind = isEagerRefKey(key) || !codeSplitting || ref.eager ? "eager" : "lazy";
|
|
91
|
+
fields.push(`${key}: ${refType(ref, kind, from)}`);
|
|
83
92
|
}
|
|
84
93
|
// Reading an absent ref is how adapters branch, so keep the key present.
|
|
85
94
|
if (!refKeys.some(isLazyRefKey))
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "filesystem-routing",
|
|
3
3
|
"description": "Router-neutral file-system routing: scans a route directory into a neutral route manifest with pluggable conventions and delivery adapters",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.4.1",
|
|
6
6
|
"author": "Ryan Carniato",
|
|
7
7
|
"homepage": "https://github.com/solidjs/filesystem-routing#readme",
|
|
8
8
|
"repository": {
|
|
@@ -44,6 +44,10 @@
|
|
|
44
44
|
"types": "./dist/api.d.ts",
|
|
45
45
|
"default": "./dist/api.js"
|
|
46
46
|
},
|
|
47
|
+
"./flags": {
|
|
48
|
+
"types": "./dist/flags.d.ts",
|
|
49
|
+
"default": "./dist/flags.js"
|
|
50
|
+
},
|
|
47
51
|
"./types": {
|
|
48
52
|
"types": "./types.d.ts"
|
|
49
53
|
}
|