@barefootjs/rust 0.30.5 → 0.31.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/README.md +11 -7
- package/dist/adapter/expr/emitters.d.ts +1 -1
- package/dist/adapter/expr/emitters.d.ts.map +1 -1
- package/dist/adapter/index.js +19 -4
- package/dist/adapter/minijinja-adapter.d.ts +4 -5
- package/dist/adapter/minijinja-adapter.d.ts.map +1 -1
- package/dist/index.js +21 -7
- package/dist/render-divergences.d.ts.map +1 -1
- package/dist/vite.d.ts +38 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +6738 -0
- package/package.json +22 -9
- package/runtime/src/runtime.rs +54 -6
- package/src/__tests__/minijinja-adapter-unit.test.ts +146 -0
- package/src/__tests__/vite.test.ts +126 -0
- package/src/adapter/expr/emitters.ts +33 -9
- package/src/adapter/minijinja-adapter.ts +42 -10
- package/src/render-divergences.ts +13 -15
- package/src/vite.ts +217 -0
- package/dist/build.d.ts +0 -29
- package/dist/build.d.ts.map +0 -1
- package/dist/build.js +0 -189253
- package/src/build.ts +0 -38
package/src/vite.ts
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@barefootjs/rust/vite` — a minijinja(Rust)-specific COMPOSITION of core
|
|
3
|
+
* `@barefootjs/vite`'s `barefoot()`, mirroring `@barefootjs/go-template/
|
|
4
|
+
* vite`'s, `@barefootjs/hono/vite`'s, `@barefootjs/blade/vite`'s,
|
|
5
|
+
* `@barefootjs/jinja/vite`'s, and `@barefootjs/erb/vite`'s shape and naming
|
|
6
|
+
* (`barefoot`, named AND default export; a user never passes `adapter`,
|
|
7
|
+
* this constructs `MinijinjaAdapter` itself).
|
|
8
|
+
*
|
|
9
|
+
* import { barefoot } from '@barefootjs/rust/vite'
|
|
10
|
+
*
|
|
11
|
+
* export default defineConfig({
|
|
12
|
+
* base: '/integrations/axum/client/',
|
|
13
|
+
* build: { outDir: 'dist/client' },
|
|
14
|
+
* plugins: barefoot({
|
|
15
|
+
* components: ['../shared/components', '../shared/blog'],
|
|
16
|
+
* templates: 'dist/templates',
|
|
17
|
+
* }),
|
|
18
|
+
* })
|
|
19
|
+
*
|
|
20
|
+
* ## Why this needs no `afterEmit`-driven type-combination step (unlike Go)
|
|
21
|
+
*
|
|
22
|
+
* `@barefootjs/go-template/vite` exists mainly to combine every discovered
|
|
23
|
+
* file's `types` fragment into ONE compilable `components.go` — Go's
|
|
24
|
+
* per-file fragments assume a shared `randomID` helper and a single package
|
|
25
|
+
* header, so they are not independently valid Go source, and an unused
|
|
26
|
+
* import fails the build outright (see that module's docstring).
|
|
27
|
+
* `MinijinjaAdapter.generate()` never produces a `types` section at all
|
|
28
|
+
* (minijinja templates have no JS-style imports/types/exports to combine —
|
|
29
|
+
* see `minijinja-adapter.ts`'s `generate()`, whose `sections.types` is
|
|
30
|
+
* always `''`), so there is nothing across files to stitch together, and no
|
|
31
|
+
* unused-import failure mode to guard against either. So this composition's
|
|
32
|
+
* core job is just what core's `barefoot()` already does: construct
|
|
33
|
+
* `MinijinjaAdapter` and hand it to core.
|
|
34
|
+
*
|
|
35
|
+
* ## No `adapterOptions` either — the other thing Go/Hono still need
|
|
36
|
+
*
|
|
37
|
+
* `MinijinjaAdapterOptions` has exactly two fields, `clientJsBasePath` and
|
|
38
|
+
* `barefootJsPath` — and `MinijinjaAdapter.generateScriptRegistrations`
|
|
39
|
+
* only falls back to them when `scriptAssets` is `undefined` (i.e. the
|
|
40
|
+
* adapter is invoked without this Vite plugin). Core's `barefoot()` plugin
|
|
41
|
+
* ALWAYS passes a resolved `scriptAssets` array (build: manifest-hashed;
|
|
42
|
+
* dev: origin-based — see `plugin.ts`), so that fallback is dead code on
|
|
43
|
+
* every Vite-driven build.
|
|
44
|
+
* Unlike Go (`packageName`, still real) or Hono (`clientJsFilename`, still
|
|
45
|
+
* real), minijinja has no adapter option left with any effect once Vite
|
|
46
|
+
* drives the build — so this options interface simply omits the field
|
|
47
|
+
* rather than plumbing through two options that would always be ignored.
|
|
48
|
+
*
|
|
49
|
+
* ## `assets` — the one thing this DOES need, mirroring go-template/hono/blade
|
|
50
|
+
*
|
|
51
|
+
* A hand-written, non-component client bootstrap (e.g. an integration's
|
|
52
|
+
* `client/router-entry.ts`, which boots `@barefootjs/router` for the blog)
|
|
53
|
+
* isn't a `.tsx` component, so core's own discovery/`scriptAssets` machinery
|
|
54
|
+
* never sees it — but the compiled blog shell still needs a `<script src>`
|
|
55
|
+
* for it, and that URL is only knowable after Vite bundles it (dev:
|
|
56
|
+
* origin-based; build: manifest-hashed). `assets` resolves exactly that,
|
|
57
|
+
* into a generated JSON file the Rust app reads at request time (the same
|
|
58
|
+
* `dist/templates/manifest.json` — read-a-JSON-file-at-runtime — idiom this
|
|
59
|
+
* Rust binary already uses for `ssrDefaults`; unlike Go/Hono there is no
|
|
60
|
+
* compile step on the Rust side that needs the URL baked in ahead of time
|
|
61
|
+
* — a plain JSON file read at startup is enough — no generated Go/TS
|
|
62
|
+
* source needed). See `@barefootjs/go-template/vite`'s docstring for the
|
|
63
|
+
* full "why a companion config-capture plugin" rationale (`afterEmit`'s
|
|
64
|
+
* `AfterEmitContext` is deliberately narrow — no `ResolvedConfig`, no dev
|
|
65
|
+
* origin, no manifest — so a tiny second plugin captures those via its own
|
|
66
|
+
* `configResolved`/`configureServer` hooks for `afterEmit`, in the same
|
|
67
|
+
* closure, to read).
|
|
68
|
+
*/
|
|
69
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises'
|
|
70
|
+
import { dirname, resolve } from 'node:path'
|
|
71
|
+
import type { Plugin, ResolvedConfig, ViteDevServer } from 'vite'
|
|
72
|
+
import { barefoot as coreBarefoot } from '@barefootjs/vite'
|
|
73
|
+
import type { AfterEmitContext } from '@barefootjs/vite'
|
|
74
|
+
import { devModuleUrl, loadManifest, resolveDevOrigin, resolveScriptAssets, toPosixRelative } from '@barefootjs/vite'
|
|
75
|
+
import { MinijinjaAdapter } from './adapter/index.ts'
|
|
76
|
+
|
|
77
|
+
export interface RustViteOptions {
|
|
78
|
+
/** Source directories to scan for `.tsx` components, relative to the
|
|
79
|
+
* Vite project root (or absolute). */
|
|
80
|
+
components: string[]
|
|
81
|
+
/** Where compiled `.j2` templates and `ssrDefaults` land — relative to
|
|
82
|
+
* the Vite project root (or absolute). This is a backend source
|
|
83
|
+
* directory the Rust binary reads, NOT `build.outDir` (Vite's
|
|
84
|
+
* client-asset output). */
|
|
85
|
+
templates: string
|
|
86
|
+
/**
|
|
87
|
+
* Extra, non-component script entries whose Vite-resolved URL (dev:
|
|
88
|
+
* origin-based; production: content-hashed manifest path) should be
|
|
89
|
+
* exposed to the Rust app as a generated JSON asset map — e.g. a
|
|
90
|
+
* hand-written client bootstrap script that isn't a `.tsx` component, so
|
|
91
|
+
* it never goes through core's discovery/`scriptAssets` machinery, but
|
|
92
|
+
* still needs a `<script src="...">` URL only knowable after bundling.
|
|
93
|
+
*
|
|
94
|
+
* Keyed by the identifier the resolved URL should appear under in the
|
|
95
|
+
* generated map; values are entry paths relative to the Vite project
|
|
96
|
+
* root. You must ALSO register the same path as a Rollup entry yourself
|
|
97
|
+
* via stock `build.rollupOptions.input` — this plugin never adds
|
|
98
|
+
* bundling configuration on your behalf; this option only resolves the
|
|
99
|
+
* URL Vite already bundled it to, it doesn't request the bundling.
|
|
100
|
+
*/
|
|
101
|
+
assets?: Record<string, string>
|
|
102
|
+
/** Output path for the generated JSON asset map, relative to the Vite
|
|
103
|
+
* project root. Default: 'dist/bf-assets.json'. Ignored when `assets`
|
|
104
|
+
* is empty. Placed under `dist/` (already gitignored) rather than
|
|
105
|
+
* committed like Go's `bf_assets.go`: the Rust app reads this file at
|
|
106
|
+
* STARTUP, so — unlike Go, which must compile a static map into the
|
|
107
|
+
* binary — there is nothing to commit; a fresh copy is generated on
|
|
108
|
+
* every build (dev AND production) and never checked in. */
|
|
109
|
+
assetsOutputFile?: string
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** write-if-changed: writes `content` to `absPath` only if it differs from
|
|
113
|
+
* what's already there, logging `label` when it actually wrote. Avoids
|
|
114
|
+
* touching mtime (and so falsely tripping a file watcher) on a pass that
|
|
115
|
+
* produced byte-identical output. */
|
|
116
|
+
async function writeIfChanged(absPath: string, content: string, label: string): Promise<void> {
|
|
117
|
+
const prev = await readFile(absPath, 'utf-8').catch(() => null)
|
|
118
|
+
if (prev === content) return
|
|
119
|
+
// Default `assetsOutputFile` lives under `dist/` — a directory `vite
|
|
120
|
+
// build` may not have created yet on a clean checkout — so ensure it
|
|
121
|
+
// exists before writing.
|
|
122
|
+
await mkdir(dirname(absPath), { recursive: true })
|
|
123
|
+
await writeFile(absPath, content)
|
|
124
|
+
console.log(`Generated: ${label}`)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Resolves ONE asset entry's URL for the current pass: manifest-hashed for
|
|
128
|
+
* `mode: 'build'`, dev-origin-based for `mode: 'dev'`. Throws with an
|
|
129
|
+
* actionable message when the entry isn't in the build manifest — almost
|
|
130
|
+
* always means the caller forgot to also add it to
|
|
131
|
+
* `build.rollupOptions.input`. */
|
|
132
|
+
function resolveAssetUrl(
|
|
133
|
+
ctx: AfterEmitContext,
|
|
134
|
+
config: ResolvedConfig,
|
|
135
|
+
devServer: ViteDevServer | undefined,
|
|
136
|
+
entryRelPath: string,
|
|
137
|
+
manifest: Record<string, { file: string }> | undefined,
|
|
138
|
+
): string {
|
|
139
|
+
const absPath = resolve(ctx.projectDir, entryRelPath)
|
|
140
|
+
if (ctx.mode === 'dev') {
|
|
141
|
+
if (!devServer) throw new Error(`[rust/vite] asset "${entryRelPath}": dev server not ready`)
|
|
142
|
+
return devModuleUrl(config, resolveDevOrigin(devServer), absPath)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const manifestKey = toPosixRelative(config.root, absPath)
|
|
146
|
+
const [url] = resolveScriptAssets(manifest ?? {}, manifestKey, config.base)
|
|
147
|
+
if (!url) {
|
|
148
|
+
throw new Error(
|
|
149
|
+
`[rust/vite] asset "${entryRelPath}" was not found in the build manifest. ` +
|
|
150
|
+
`Did you also add it to build.rollupOptions.input?`,
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
return url
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Builds and write-if-changed's the generated JSON asset map. No-op when
|
|
157
|
+
* `assets` is empty. */
|
|
158
|
+
async function writeAssetMap(
|
|
159
|
+
ctx: AfterEmitContext,
|
|
160
|
+
config: ResolvedConfig,
|
|
161
|
+
devServer: ViteDevServer | undefined,
|
|
162
|
+
assets: Record<string, string>,
|
|
163
|
+
assetsOutputFile: string,
|
|
164
|
+
): Promise<void> {
|
|
165
|
+
const keys = Object.keys(assets)
|
|
166
|
+
if (keys.length === 0) return
|
|
167
|
+
|
|
168
|
+
const manifest = ctx.mode === 'build' ? await loadManifest(ctx.outDir, config.build.manifest) : undefined
|
|
169
|
+
|
|
170
|
+
const resolved: Record<string, string> = {}
|
|
171
|
+
for (const name of keys) {
|
|
172
|
+
resolved[name] = resolveAssetUrl(ctx, config, devServer, assets[name]!, manifest)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const content = `${JSON.stringify(resolved, null, 2)}\n`
|
|
176
|
+
await writeIfChanged(resolve(ctx.projectDir, assetsOutputFile), content, assetsOutputFile)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function barefoot(options: RustViteOptions): Plugin[] {
|
|
180
|
+
const assets = options.assets ?? {}
|
|
181
|
+
const assetsOutputFile = options.assetsOutputFile ?? 'dist/bf-assets.json'
|
|
182
|
+
|
|
183
|
+
// Populated by `rustAssetsConfigCapture` below (only added to the
|
|
184
|
+
// returned array when `assets` is non-empty). Read by `afterEmit` — see
|
|
185
|
+
// `@barefootjs/go-template/vite`'s identical mechanism for the ordering
|
|
186
|
+
// guarantee (`configResolved`/`configureServer` always run before the
|
|
187
|
+
// eager pass that triggers `afterEmit`, for both build and dev).
|
|
188
|
+
let resolvedConfig: ResolvedConfig | undefined
|
|
189
|
+
let devServer: ViteDevServer | undefined
|
|
190
|
+
|
|
191
|
+
const core = coreBarefoot({
|
|
192
|
+
adapter: new MinijinjaAdapter(),
|
|
193
|
+
components: options.components,
|
|
194
|
+
templates: options.templates,
|
|
195
|
+
async afterEmit(ctx) {
|
|
196
|
+
if (Object.keys(assets).length > 0 && resolvedConfig) {
|
|
197
|
+
await writeAssetMap(ctx, resolvedConfig, devServer, assets, assetsOutputFile)
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
if (Object.keys(assets).length === 0) return [core]
|
|
203
|
+
|
|
204
|
+
const rustAssetsConfigCapture: Plugin = {
|
|
205
|
+
name: 'barefoot-rust-assets-config-capture',
|
|
206
|
+
configResolved(config) {
|
|
207
|
+
resolvedConfig = config
|
|
208
|
+
},
|
|
209
|
+
configureServer(server) {
|
|
210
|
+
devServer = server
|
|
211
|
+
},
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return [core, rustAssetsConfigCapture]
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export { barefoot as default }
|
package/dist/build.d.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { BuildOptions } from '@barefootjs/jsx';
|
|
2
|
-
import { MinijinjaAdapter } from './adapter/index.ts';
|
|
3
|
-
import type { MinijinjaAdapterOptions } from './adapter/index.ts';
|
|
4
|
-
export interface MinijinjaBuildOptions extends BuildOptions {
|
|
5
|
-
/** Adapter-specific options passed to MinijinjaAdapter */
|
|
6
|
-
adapterOptions?: MinijinjaAdapterOptions;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Create a BarefootBuildConfig for minijinja (Jinja2-compatible) template
|
|
10
|
-
* projects.
|
|
11
|
-
*
|
|
12
|
-
* Uses structural typing — does not import BarefootBuildConfig to avoid a
|
|
13
|
-
* circular dependency between @barefootjs/rust and @barefootjs/cli.
|
|
14
|
-
*/
|
|
15
|
-
export declare function createConfig(options?: MinijinjaBuildOptions): {
|
|
16
|
-
adapter: MinijinjaAdapter;
|
|
17
|
-
paths: import("@barefootjs/jsx").BarefootPaths | undefined;
|
|
18
|
-
components: string[] | undefined;
|
|
19
|
-
outDir: string | undefined;
|
|
20
|
-
minify: boolean | undefined;
|
|
21
|
-
contentHash: boolean | undefined;
|
|
22
|
-
externals: Record<string, import("@barefootjs/jsx").ExternalSpec> | undefined;
|
|
23
|
-
externalsBasePath: string | undefined;
|
|
24
|
-
bundleEntries: import("@barefootjs/jsx").BundleEntry[] | undefined;
|
|
25
|
-
localImportPrefixes: string[] | undefined;
|
|
26
|
-
outputLayout: import("@barefootjs/jsx").OutputLayout;
|
|
27
|
-
postBuild: ((ctx: import("@barefootjs/jsx").PostBuildContext) => Promise<void> | void) | undefined;
|
|
28
|
-
};
|
|
29
|
-
//# sourceMappingURL=build.d.ts.map
|
package/dist/build.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAA;AAEjE,MAAM,WAAW,qBAAsB,SAAQ,YAAY;IACzD,0DAA0D;IAC1D,cAAc,CAAC,EAAE,uBAAuB,CAAA;CACzC;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,qBAA0B;IAE5D,OAAO;IACP,KAAK;IACL,UAAU;IACV,MAAM;IACN,MAAM;IACN,WAAW;IACX,SAAS;IACT,iBAAiB;IACjB,aAAa;IACb,mBAAmB;IACnB,YAAY;IAKZ,SAAS;EAEZ"}
|