@fougere/vite 0.13.1-alpha.0 → 0.14.0-alpha.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/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +60 -11
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
- package/src/index.ts +70 -12
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,13 @@ export interface FougereViteOptions {
|
|
|
10
10
|
keepClassNames?: boolean;
|
|
11
11
|
/** Extra identifiers to reserve, for entities that do not live under `fronds/`. */
|
|
12
12
|
reserved?: string[];
|
|
13
|
+
/**
|
|
14
|
+
* Where the fronds are, from the app — `'../..'` in a workspace, whose `fronds/` sit at its
|
|
15
|
+
* root. The same key `@fougere/nuxt` reads.
|
|
16
|
+
*/
|
|
17
|
+
root?: string;
|
|
18
|
+
/** Set false when the host serves the call envelope itself. */
|
|
19
|
+
serve?: boolean;
|
|
13
20
|
}
|
|
14
21
|
export declare function fougere(options?: FougereViteOptions): Plugin;
|
|
15
22
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,OAAO,EAAE,KAAK,WAAW,EAAuB,MAAM,eAAe,CAAC;AAEtE,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,GAAE,WAAiC,GAAG,MAAM,EAAE,CAgBpG;AAED,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,WAAW,kBAAkB;IACjC,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,iDAAiD;IACjD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AA4FD,wBAAgB,OAAO,CAAC,OAAO,GAAE,kBAAuB,GAAG,MAAM,CAgEhE"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readdirSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
2
|
-
import { join, dirname } from 'node:path';
|
|
2
|
+
import { join, dirname, resolve } from 'node:path';
|
|
3
3
|
import { RUNTIME_PACKAGES } from '@fougere/compiler';
|
|
4
4
|
import { DEFAULT_CONVENTIONS } from '@fougere/core';
|
|
5
5
|
/** The entity names a build must not rename, read off the filesystem. */
|
|
@@ -23,6 +23,47 @@ export function entityNamesIn(root, conventions = DEFAULT_CONVENTIONS) {
|
|
|
23
23
|
return [...names];
|
|
24
24
|
}
|
|
25
25
|
export { RUNTIME_PACKAGES } from '@fougere/compiler';
|
|
26
|
+
/** What the browser primitives call, and the web handler that answers each. */
|
|
27
|
+
const DOORS = { '/_fougere/call': 'call', '/_fougere/session': 'session' };
|
|
28
|
+
/**
|
|
29
|
+
* The call envelope, answered by the dev server itself: a Vite app has no server of its own, and
|
|
30
|
+
* the browser calls its own origin. The app boots at the first call, from `fougere.config.ts`, so
|
|
31
|
+
* `fronds:` decides whether a frond answers here or a process away — the same line as under Nuxt.
|
|
32
|
+
*/
|
|
33
|
+
function serveDoors() {
|
|
34
|
+
return (req, res, next) => {
|
|
35
|
+
const path = (req.url ?? '').split('?')[0];
|
|
36
|
+
const door = DOORS[path];
|
|
37
|
+
if (!door)
|
|
38
|
+
return next();
|
|
39
|
+
void (async () => {
|
|
40
|
+
try {
|
|
41
|
+
const web = await import('@fougere/app/web');
|
|
42
|
+
const response = await web[door](await requestOf(req));
|
|
43
|
+
res.statusCode = response.status;
|
|
44
|
+
response.headers.forEach((value, key) => res.setHeader(key, value));
|
|
45
|
+
res.end(Buffer.from(await response.arrayBuffer()));
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
res.statusCode = 500;
|
|
49
|
+
res.setHeader('content-type', 'application/json');
|
|
50
|
+
res.end(JSON.stringify({ code: 'INTERNAL_ERROR', message: error.message }));
|
|
51
|
+
}
|
|
52
|
+
})();
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
async function requestOf(req) {
|
|
56
|
+
const chunks = [];
|
|
57
|
+
for await (const chunk of req)
|
|
58
|
+
chunks.push(chunk);
|
|
59
|
+
const headers = new Headers(Object.entries(req.headers)
|
|
60
|
+
.filter((entry) => typeof entry[1] === 'string'));
|
|
61
|
+
return new Request(`http://${req.headers.host ?? 'localhost'}${req.url}`, {
|
|
62
|
+
method: req.method,
|
|
63
|
+
headers,
|
|
64
|
+
...(chunks.length ? { body: Buffer.concat(chunks) } : {}),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
26
67
|
/**
|
|
27
68
|
* Where the facade types land — the same place `fougere build` puts them, so a host that runs the
|
|
28
69
|
* command and one that only starts a dev server read one file and not two.
|
|
@@ -41,7 +82,7 @@ const NAMES = '.fougere/names.generated.d.ts';
|
|
|
41
82
|
* A scan that fails is not a failure of the dev server: the types fall back to every code there
|
|
42
83
|
* is, which is what a client had before this existed.
|
|
43
84
|
*/
|
|
44
|
-
async function writeDoors(root) {
|
|
85
|
+
async function writeDoors(app, root) {
|
|
45
86
|
try {
|
|
46
87
|
const { scanProject, emitFacade, emitNames, frondAliases } = await import('@fougere/compiler');
|
|
47
88
|
const { setModuleLoader } = await import('@fougere/core/node');
|
|
@@ -57,25 +98,30 @@ async function writeDoors(root) {
|
|
|
57
98
|
});
|
|
58
99
|
setModuleLoader((filePath) => jiti.import(filePath));
|
|
59
100
|
const { loadConfig } = await import('@fougere/core/node');
|
|
60
|
-
const out = join(
|
|
101
|
+
const out = join(app, FACADE);
|
|
61
102
|
mkdirSync(dirname(out), { recursive: true });
|
|
62
103
|
const scan = await scanProject(root);
|
|
63
104
|
writeFileSync(out, emitFacade(scan, { outFile: out }));
|
|
64
105
|
// What a config may NAME, from the same scan: the sources are the config's own keys, which
|
|
65
106
|
// no scan can find, and a project without one states none.
|
|
66
107
|
const config = await loadConfig(root).catch(() => ({}));
|
|
67
|
-
writeFileSync(join(
|
|
108
|
+
writeFileSync(join(app, NAMES), emitNames(scan, { sources: Object.keys(config.sources ?? {}) }));
|
|
68
109
|
}
|
|
69
110
|
catch { /* a host with no fronds, or a scan that could not run */ }
|
|
70
111
|
}
|
|
71
112
|
export function fougere(options = {}) {
|
|
72
113
|
const external = [...new Set([...RUNTIME_PACKAGES, ...(options.external ?? [])])];
|
|
73
|
-
// Where `config` said the
|
|
74
|
-
|
|
114
|
+
// Where `config` said the app is, so `buildStart` writes beside the alias it set — and where
|
|
115
|
+
// its fronds are, which a workspace puts two levels up.
|
|
116
|
+
let app = process.cwd();
|
|
117
|
+
const frondsOf = (from) => resolve(from, options.root ?? '.');
|
|
75
118
|
return {
|
|
76
119
|
name: 'fougere',
|
|
77
120
|
configureServer(server) {
|
|
78
|
-
|
|
121
|
+
const root = server.config?.root ?? app;
|
|
122
|
+
void writeDoors(root, frondsOf(root));
|
|
123
|
+
if (options.serve !== false)
|
|
124
|
+
server.middlewares.use(serveDoors());
|
|
79
125
|
},
|
|
80
126
|
/**
|
|
81
127
|
* AWAITED, and that is the whole point: the bundler resolves `@fronds/facade` right after
|
|
@@ -83,7 +129,7 @@ export function fougere(options = {}) {
|
|
|
83
129
|
* .fougere/facade.generated.ts`, measured on `demos/react-router-blog`.
|
|
84
130
|
*/
|
|
85
131
|
async buildStart() {
|
|
86
|
-
await writeDoors(
|
|
132
|
+
await writeDoors(app, frondsOf(app));
|
|
87
133
|
},
|
|
88
134
|
/** `order. */
|
|
89
135
|
config: {
|
|
@@ -94,18 +140,21 @@ export function fougere(options = {}) {
|
|
|
94
140
|
// A page IMPORTS its facade, so the alias sits beside the file that declares them: a
|
|
95
141
|
// project that never generated it fails to resolve rather than losing its types in
|
|
96
142
|
// silence.
|
|
97
|
-
|
|
143
|
+
app = resolve(config.root ?? app);
|
|
144
|
+
// Read by the boot, which runs in this process and has no other way to learn it — the
|
|
145
|
+
// variable `@fougere/nuxt` sets for the same reason.
|
|
146
|
+
process.env.FOUGERE_ROOT = frondsOf(app);
|
|
98
147
|
config.resolve ??= {};
|
|
99
148
|
// Vite admits BOTH shapes and a host picks one: SvelteKit states an array of
|
|
100
149
|
// `{ find, replacement }`, and spreading an object over it made rolldown refuse the
|
|
101
150
|
// whole plugin — `StringExpected … on BindingViteAliasPluginAlias.replacement`.
|
|
102
|
-
const facade = join(
|
|
151
|
+
const facade = join(app, FACADE);
|
|
103
152
|
config.resolve.alias = Array.isArray(config.resolve.alias)
|
|
104
153
|
? [...config.resolve.alias, { find: '@fronds/facade', replacement: facade }]
|
|
105
154
|
: { ...config.resolve.alias, '@fronds/facade': facade };
|
|
106
155
|
if (options.keepClassNames === false)
|
|
107
156
|
return;
|
|
108
|
-
const reserved = [...(options.reserved ?? []), ...entityNamesIn(
|
|
157
|
+
const reserved = [...(options.reserved ?? []), ...entityNamesIn(frondsOf(app))];
|
|
109
158
|
if (reserved.length === 0)
|
|
110
159
|
return;
|
|
111
160
|
config.build ??= {};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fougere/vite",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0-alpha.0",
|
|
4
4
|
"description": "The Vite plugin: what any Vite-built host must know to run a Fougere app.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fougere",
|
|
@@ -30,8 +30,9 @@
|
|
|
30
30
|
"src"
|
|
31
31
|
],
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@fougere/
|
|
34
|
-
"@fougere/
|
|
33
|
+
"@fougere/app": "0.14.0-alpha.0",
|
|
34
|
+
"@fougere/compiler": "0.14.0-alpha.0",
|
|
35
|
+
"@fougere/core": "0.14.0-alpha.0",
|
|
35
36
|
"jiti": "^2.7.0",
|
|
36
37
|
"terser": "^5.44.0"
|
|
37
38
|
},
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/** `@fougere/vite` — the one place a Vite-built host is told what a Fougere app needs. */
|
|
2
2
|
import type { FougereConfig } from '@fougere/core';
|
|
3
3
|
import { readdirSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
4
|
-
import {
|
|
4
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
5
|
+
import { join, dirname, resolve } from 'node:path';
|
|
5
6
|
import type { Plugin } from 'vite';
|
|
6
7
|
import { RUNTIME_PACKAGES } from '@fougere/compiler';
|
|
7
8
|
import { type Conventions, DEFAULT_CONVENTIONS } from '@fougere/core';
|
|
@@ -34,6 +35,56 @@ export interface FougereViteOptions {
|
|
|
34
35
|
keepClassNames?: boolean;
|
|
35
36
|
/** Extra identifiers to reserve, for entities that do not live under `fronds/`. */
|
|
36
37
|
reserved?: string[];
|
|
38
|
+
/**
|
|
39
|
+
* Where the fronds are, from the app — `'../..'` in a workspace, whose `fronds/` sit at its
|
|
40
|
+
* root. The same key `@fougere/nuxt` reads.
|
|
41
|
+
*/
|
|
42
|
+
root?: string;
|
|
43
|
+
/** Set false when the host serves the call envelope itself. */
|
|
44
|
+
serve?: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** What the browser primitives call, and the web handler that answers each. */
|
|
48
|
+
const DOORS = { '/_fougere/call': 'call', '/_fougere/session': 'session' } as const;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The call envelope, answered by the dev server itself: a Vite app has no server of its own, and
|
|
52
|
+
* the browser calls its own origin. The app boots at the first call, from `fougere.config.ts`, so
|
|
53
|
+
* `fronds:` decides whether a frond answers here or a process away — the same line as under Nuxt.
|
|
54
|
+
*/
|
|
55
|
+
function serveDoors() {
|
|
56
|
+
return (req: IncomingMessage, res: ServerResponse, next: () => void): void => {
|
|
57
|
+
const path = (req.url ?? '').split('?')[0] as keyof typeof DOORS;
|
|
58
|
+
const door = DOORS[path];
|
|
59
|
+
if (!door) return next();
|
|
60
|
+
|
|
61
|
+
void (async () => {
|
|
62
|
+
try {
|
|
63
|
+
const web = await import('@fougere/app/web');
|
|
64
|
+
const response = await web[door](await requestOf(req));
|
|
65
|
+
res.statusCode = response.status;
|
|
66
|
+
response.headers.forEach((value, key) => res.setHeader(key, value));
|
|
67
|
+
res.end(Buffer.from(await response.arrayBuffer()));
|
|
68
|
+
} catch (error) {
|
|
69
|
+
res.statusCode = 500;
|
|
70
|
+
res.setHeader('content-type', 'application/json');
|
|
71
|
+
res.end(JSON.stringify({ code: 'INTERNAL_ERROR', message: (error as Error).message }));
|
|
72
|
+
}
|
|
73
|
+
})();
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function requestOf(req: IncomingMessage): Promise<Request> {
|
|
78
|
+
const chunks: Buffer[] = [];
|
|
79
|
+
for await (const chunk of req) chunks.push(chunk as Buffer);
|
|
80
|
+
const headers = new Headers(Object.entries(req.headers)
|
|
81
|
+
.filter((entry): entry is [string, string] => typeof entry[1] === 'string'));
|
|
82
|
+
|
|
83
|
+
return new Request(`http://${req.headers.host ?? 'localhost'}${req.url}`, {
|
|
84
|
+
method: req.method,
|
|
85
|
+
headers,
|
|
86
|
+
...(chunks.length ? { body: Buffer.concat(chunks) } : {}),
|
|
87
|
+
});
|
|
37
88
|
}
|
|
38
89
|
|
|
39
90
|
/**
|
|
@@ -55,7 +106,7 @@ const NAMES = '.fougere/names.generated.d.ts';
|
|
|
55
106
|
* A scan that fails is not a failure of the dev server: the types fall back to every code there
|
|
56
107
|
* is, which is what a client had before this existed.
|
|
57
108
|
*/
|
|
58
|
-
async function writeDoors(root: string): Promise<void> {
|
|
109
|
+
async function writeDoors(app: string, root: string): Promise<void> {
|
|
59
110
|
try {
|
|
60
111
|
const { scanProject, emitFacade, emitNames, frondAliases } = await import('@fougere/compiler');
|
|
61
112
|
const { setModuleLoader } = await import('@fougere/core/node');
|
|
@@ -72,26 +123,30 @@ async function writeDoors(root: string): Promise<void> {
|
|
|
72
123
|
});
|
|
73
124
|
setModuleLoader((filePath: string) => jiti.import(filePath) as Promise<Record<string, unknown>>);
|
|
74
125
|
const { loadConfig } = await import('@fougere/core/node');
|
|
75
|
-
const out = join(
|
|
126
|
+
const out = join(app, FACADE);
|
|
76
127
|
mkdirSync(dirname(out), { recursive: true });
|
|
77
128
|
const scan = await scanProject(root);
|
|
78
129
|
writeFileSync(out, emitFacade(scan, { outFile: out }));
|
|
79
130
|
// What a config may NAME, from the same scan: the sources are the config's own keys, which
|
|
80
131
|
// no scan can find, and a project without one states none.
|
|
81
132
|
const config = await loadConfig(root).catch(() => ({}) as FougereConfig);
|
|
82
|
-
writeFileSync(join(
|
|
133
|
+
writeFileSync(join(app, NAMES), emitNames(scan, { sources: Object.keys(config.sources ?? {}) }));
|
|
83
134
|
} catch { /* a host with no fronds, or a scan that could not run */ }
|
|
84
135
|
}
|
|
85
136
|
|
|
86
137
|
export function fougere(options: FougereViteOptions = {}): Plugin {
|
|
87
138
|
const external = [...new Set([...RUNTIME_PACKAGES, ...(options.external ?? [])])];
|
|
88
|
-
// Where `config` said the
|
|
89
|
-
|
|
139
|
+
// Where `config` said the app is, so `buildStart` writes beside the alias it set — and where
|
|
140
|
+
// its fronds are, which a workspace puts two levels up.
|
|
141
|
+
let app = process.cwd();
|
|
142
|
+
const frondsOf = (from: string) => resolve(from, options.root ?? '.');
|
|
90
143
|
|
|
91
144
|
return {
|
|
92
145
|
name: 'fougere',
|
|
93
|
-
configureServer(server: { config?: { root?: string } }) {
|
|
94
|
-
|
|
146
|
+
configureServer(server: { config?: { root?: string }; middlewares: { use(handler: ReturnType<typeof serveDoors>): void } }) {
|
|
147
|
+
const root = server.config?.root ?? app;
|
|
148
|
+
void writeDoors(root, frondsOf(root));
|
|
149
|
+
if (options.serve !== false) server.middlewares.use(serveDoors());
|
|
95
150
|
},
|
|
96
151
|
/**
|
|
97
152
|
* AWAITED, and that is the whole point: the bundler resolves `@fronds/facade` right after
|
|
@@ -99,7 +154,7 @@ export function fougere(options: FougereViteOptions = {}): Plugin {
|
|
|
99
154
|
* .fougere/facade.generated.ts`, measured on `demos/react-router-blog`.
|
|
100
155
|
*/
|
|
101
156
|
async buildStart() {
|
|
102
|
-
await writeDoors(
|
|
157
|
+
await writeDoors(app, frondsOf(app));
|
|
103
158
|
},
|
|
104
159
|
/** `order. */
|
|
105
160
|
config: {
|
|
@@ -111,19 +166,22 @@ export function fougere(options: FougereViteOptions = {}): Plugin {
|
|
|
111
166
|
// A page IMPORTS its facade, so the alias sits beside the file that declares them: a
|
|
112
167
|
// project that never generated it fails to resolve rather than losing its types in
|
|
113
168
|
// silence.
|
|
114
|
-
|
|
169
|
+
app = resolve(config.root ?? app);
|
|
170
|
+
// Read by the boot, which runs in this process and has no other way to learn it — the
|
|
171
|
+
// variable `@fougere/nuxt` sets for the same reason.
|
|
172
|
+
process.env.FOUGERE_ROOT = frondsOf(app);
|
|
115
173
|
config.resolve ??= {};
|
|
116
174
|
// Vite admits BOTH shapes and a host picks one: SvelteKit states an array of
|
|
117
175
|
// `{ find, replacement }`, and spreading an object over it made rolldown refuse the
|
|
118
176
|
// whole plugin — `StringExpected … on BindingViteAliasPluginAlias.replacement`.
|
|
119
|
-
const facade = join(
|
|
177
|
+
const facade = join(app, FACADE);
|
|
120
178
|
config.resolve.alias = Array.isArray(config.resolve.alias)
|
|
121
179
|
? [...config.resolve.alias, { find: '@fronds/facade', replacement: facade }]
|
|
122
180
|
: { ...config.resolve.alias, '@fronds/facade': facade };
|
|
123
181
|
|
|
124
182
|
if (options.keepClassNames === false) return;
|
|
125
183
|
|
|
126
|
-
const reserved = [...(options.reserved ?? []), ...entityNamesIn(
|
|
184
|
+
const reserved = [...(options.reserved ?? []), ...entityNamesIn(frondsOf(app))];
|
|
127
185
|
if (reserved.length === 0) return;
|
|
128
186
|
|
|
129
187
|
config.build ??= {};
|