@chr33s/solarflare 0.0.4 → 0.0.5

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": "@chr33s/solarflare",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -3,11 +3,18 @@ import { readFile, unlink, mkdir, writeFile } from "node:fs/promises";
3
3
  import { dirname, join } from "node:path";
4
4
  import ts from "typescript";
5
5
  import { rolldown } from "rolldown";
6
+ import type { RolldownOptions } from "rolldown";
6
7
  import { replacePlugin } from "rolldown/plugins";
7
8
  import { transform } from "lightningcss";
8
9
  import { createProgram, getDefaultExportInfo } from "./ast.ts";
9
10
  import { exists } from "./fs.ts";
10
- import { assetUrlPrefixPlugin, type BuildArgs, moduleTypes } from "./build.bundle.ts";
11
+ import {
12
+ assetUrlPrefixPlugin,
13
+ type BuildArgs,
14
+ mergeInputOptions,
15
+ mergeOutputOptions,
16
+ moduleTypes,
17
+ } from "./build.bundle.ts";
11
18
  import { parsePath } from "./paths.ts";
12
19
  import { generateClientScript } from "./console-forward.ts";
13
20
  import { createScanner } from "./build.scan.ts";
@@ -22,6 +29,7 @@ export interface BuildClientOptions {
22
29
  distClient: string;
23
30
  publicDir: string;
24
31
  chunksPath: string;
32
+ userConfig?: RolldownOptions;
25
33
  }
26
34
 
27
35
  async function remove(path: string) {
@@ -82,7 +90,7 @@ async function getComponentMeta(program: ts.Program, appDir: string, file: strin
82
90
  }
83
91
 
84
92
  export async function buildClient(options: BuildClientOptions) {
85
- const { args, rootDir, appDir, distDir, distClient, publicDir, chunksPath } = options;
93
+ const { args, rootDir, appDir, distDir, distClient, publicDir, chunksPath, userConfig } = options;
86
94
  const distClientAssets = join(distClient, "assets");
87
95
  const scanner = createScanner({ rootDir, appDir });
88
96
 
@@ -255,80 +263,90 @@ export async function buildClient(options: BuildClientOptions) {
255
263
 
256
264
  const packageImports = await scanner.getPackageImports();
257
265
 
258
- const bundle = await rolldown({
259
- input,
260
- platform: "browser",
261
- tsconfig: true,
262
- moduleTypes,
263
- plugins: [
264
- replacePlugin({
265
- "globalThis.__SF_DEV__": JSON.stringify(!args.production),
266
- }),
266
+ const bundle = await rolldown(
267
+ mergeInputOptions(
267
268
  {
268
- name: "raw-css-loader",
269
- resolveId(source: string, importer: string | undefined) {
270
- if (source.endsWith("?raw") && source.includes(".css")) {
271
- const realPath = source.replace(/\?raw$/, "");
272
- if (importer) {
273
- const importerDir = importer.split("/").slice(0, -1).join("/");
274
- return {
275
- id: join(importerDir, realPath) + "?raw",
276
- external: false,
277
- };
278
- }
279
- return { id: realPath + "?raw", external: false };
280
- }
281
- return null;
269
+ input,
270
+ platform: "browser",
271
+ tsconfig: true,
272
+ moduleTypes,
273
+ plugins: [
274
+ replacePlugin({
275
+ "globalThis.__SF_DEV__": JSON.stringify(!args.production),
276
+ }),
277
+ {
278
+ name: "raw-css-loader",
279
+ resolveId(source: string, importer: string | undefined) {
280
+ if (source.endsWith("?raw") && source.includes(".css")) {
281
+ const realPath = source.replace(/\?raw$/, "");
282
+ if (importer) {
283
+ const importerDir = importer.split("/").slice(0, -1).join("/");
284
+ return {
285
+ id: join(importerDir, realPath) + "?raw",
286
+ external: false,
287
+ };
288
+ }
289
+ return { id: realPath + "?raw", external: false };
290
+ }
291
+ return null;
292
+ },
293
+ async load(id: string) {
294
+ if (id.endsWith("?raw")) {
295
+ const realPath = id.replace(/\?raw$/, "");
296
+ try {
297
+ const content = await readFile(realPath, "utf-8");
298
+ return {
299
+ code: /* tsx */ `export default ${JSON.stringify(content)};`,
300
+ moduleType: "js",
301
+ };
302
+ } catch {
303
+ console.warn(`[raw-css-loader] Could not load: ${realPath}`);
304
+ return { code: `export default "";`, moduleType: "js" };
305
+ }
306
+ }
307
+ return null;
308
+ },
309
+ },
310
+ assetUrlPrefixPlugin,
311
+ ],
312
+ resolve: {
313
+ alias: packageImports,
282
314
  },
283
- async load(id: string) {
284
- if (id.endsWith("?raw")) {
285
- const realPath = id.replace(/\?raw$/, "");
286
- try {
287
- const content = await readFile(realPath, "utf-8");
288
- return {
289
- code: /* tsx */ `export default ${JSON.stringify(content)};`,
290
- moduleType: "js",
291
- };
292
- } catch {
293
- console.warn(`[raw-css-loader] Could not load: ${realPath}`);
294
- return { code: `export default "";`, moduleType: "js" };
295
- }
296
- }
297
- return null;
315
+ transform: {
316
+ target: "es2020",
317
+ jsx: {
318
+ runtime: "automatic",
319
+ development: !args.production,
320
+ },
298
321
  },
299
322
  },
300
- assetUrlPrefixPlugin,
301
- ],
302
- resolve: {
303
- alias: packageImports,
304
- },
305
- transform: {
306
- target: "es2020",
307
- jsx: {
308
- runtime: "automatic",
309
- development: !args.production,
310
- },
311
- },
312
- });
313
-
314
- await bundle.write({
315
- dir: distClientAssets,
316
- format: "esm",
317
- entryFileNames: "[name].js",
318
- minify: args.production,
319
- chunkFileNames: "[name]-[hash].js",
320
- assetFileNames: "[name]-[hash][extname]",
321
- codeSplitting: {
322
- minSize: 20000,
323
- groups: [
324
- {
325
- name: "vendor",
326
- test: /node_modules/,
323
+ userConfig,
324
+ ),
325
+ );
326
+
327
+ await bundle.write(
328
+ mergeOutputOptions(
329
+ {
330
+ dir: distClientAssets,
331
+ format: "esm",
332
+ entryFileNames: "[name].js",
333
+ minify: args.production,
334
+ chunkFileNames: "[name]-[hash].js",
335
+ assetFileNames: "[name]-[hash][extname]",
336
+ codeSplitting: {
337
+ minSize: 20000,
338
+ groups: [
339
+ {
340
+ name: "vendor",
341
+ test: /node_modules/,
342
+ },
343
+ ],
327
344
  },
328
- ],
329
- },
330
- ...(args.sourcemap && { sourcemap: true }),
331
- });
345
+ ...(args.sourcemap && { sourcemap: true }),
346
+ },
347
+ userConfig,
348
+ ),
349
+ );
332
350
 
333
351
  if (cssOutputsByBase.size > 0) {
334
352
  const emittedCss = new Set(cssOutputsByBase.values());
@@ -2,8 +2,15 @@ import { mkdir, unlink, writeFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import { glob } from "node:fs/promises";
4
4
  import { rolldown } from "rolldown";
5
+ import type { RolldownOptions } from "rolldown";
5
6
  import { createProgram } from "./ast.ts";
6
- import { assetUrlPrefixPlugin, type BuildArgs, moduleTypes } from "./build.bundle.ts";
7
+ import {
8
+ assetUrlPrefixPlugin,
9
+ type BuildArgs,
10
+ mergeInputOptions,
11
+ mergeOutputOptions,
12
+ moduleTypes,
13
+ } from "./build.bundle.ts";
7
14
  import { createScanner } from "./build.scan.ts";
8
15
  import { validateRoutes, generateRoutesTypeFile } from "./build.validate.ts";
9
16
  import { generateModulesFile } from "./build.emit-manifests.ts";
@@ -16,10 +23,12 @@ export interface BuildServerOptions {
16
23
  modulesPath: string;
17
24
  chunksPath: string;
18
25
  routesTypePath: string;
26
+ userConfig?: RolldownOptions;
19
27
  }
20
28
 
21
29
  export async function buildServer(options: BuildServerOptions) {
22
- const { args, rootDir, appDir, distServer, modulesPath, chunksPath, routesTypePath } = options;
30
+ const { args, rootDir, appDir, distServer, modulesPath, chunksPath, routesTypePath, userConfig } =
31
+ options;
23
32
  const scanner = createScanner({ rootDir, appDir });
24
33
 
25
34
  console.log("🔍 Scanning for route modules...");
@@ -73,51 +82,61 @@ export async function buildServer(options: BuildServerOptions) {
73
82
 
74
83
  const packageImports = await scanner.getPackageImports();
75
84
 
76
- const bundle = await rolldown({
77
- input: join(appDir, "index.ts"),
78
- platform: "node",
79
- tsconfig: true,
80
- moduleTypes,
81
- external: [
82
- "cloudflare:workers",
83
- "preact",
84
- "preact/hooks",
85
- "preact/compat",
86
- "preact/jsx-runtime",
87
- "preact/jsx-dev-runtime",
88
- "preact/debug",
89
- "@preact/signals",
90
- "@preact/signals-core",
91
- "@preact/signals-debug",
92
- "preact-render-to-string",
93
- "preact-render-to-string/stream",
94
- "preact-custom-element",
95
- ],
96
- resolve: {
97
- alias: {
98
- ...packageImports,
99
- ".modules.generated": modulesPath,
100
- ".chunks.generated.json": chunksPath,
85
+ const bundle = await rolldown(
86
+ mergeInputOptions(
87
+ {
88
+ input: join(appDir, "index.ts"),
89
+ platform: "node",
90
+ tsconfig: true,
91
+ moduleTypes,
92
+ external: [
93
+ "cloudflare:workers",
94
+ "preact",
95
+ "preact/hooks",
96
+ "preact/compat",
97
+ "preact/jsx-runtime",
98
+ "preact/jsx-dev-runtime",
99
+ "preact/debug",
100
+ "@preact/signals",
101
+ "@preact/signals-core",
102
+ "@preact/signals-debug",
103
+ "preact-render-to-string",
104
+ "preact-render-to-string/stream",
105
+ "preact-custom-element",
106
+ ],
107
+ resolve: {
108
+ alias: {
109
+ ...packageImports,
110
+ ".modules.generated": modulesPath,
111
+ ".chunks.generated.json": chunksPath,
112
+ },
113
+ },
114
+ plugins: [assetUrlPrefixPlugin],
115
+ transform: {
116
+ jsx: {
117
+ runtime: "automatic",
118
+ development: false,
119
+ },
120
+ },
101
121
  },
102
- },
103
- plugins: [assetUrlPrefixPlugin],
104
- transform: {
105
- jsx: {
106
- runtime: "automatic",
107
- development: false,
122
+ userConfig,
123
+ ),
124
+ );
125
+
126
+ await bundle.write(
127
+ mergeOutputOptions(
128
+ {
129
+ dir: distServer,
130
+ format: "esm",
131
+ codeSplitting: false,
132
+ entryFileNames: "index.js",
133
+ assetFileNames: "[name]-[hash][extname]",
134
+ minify: args.production,
135
+ ...(args.sourcemap && { sourcemap: true }),
108
136
  },
109
- },
110
- });
111
-
112
- await bundle.write({
113
- dir: distServer,
114
- format: "esm",
115
- codeSplitting: false,
116
- entryFileNames: "index.js",
117
- assetFileNames: "[name]-[hash][extname]",
118
- minify: args.production,
119
- ...(args.sourcemap && { sourcemap: true }),
120
- });
137
+ userConfig,
138
+ ),
139
+ );
121
140
 
122
141
  await bundle.close();
123
142
 
@@ -1,4 +1,82 @@
1
- import type { ModuleTypes, NormalizedOutputOptions, OutputBundle } from "rolldown";
1
+ import { join } from "node:path";
2
+ import type {
3
+ ExternalOption,
4
+ InputOptions,
5
+ ModuleTypes,
6
+ NormalizedOutputOptions,
7
+ OutputBundle,
8
+ OutputOptions,
9
+ Plugin,
10
+ RolldownOptions,
11
+ RolldownPluginOption,
12
+ } from "rolldown";
13
+ import { exists } from "./fs.ts";
14
+
15
+ /** Load a `rolldown.config.ts` from `rootDir` if it exists. */
16
+ export async function loadUserConfig(rootDir: string): Promise<RolldownOptions | undefined> {
17
+ const configPath = join(rootDir, "rolldown.config.ts");
18
+ if (!(await exists(configPath))) return undefined;
19
+ const { loadConfig } = await import("rolldown/config");
20
+ const exported = await loadConfig(configPath);
21
+ if (typeof exported === "function") return (await exported({})) as RolldownOptions;
22
+ if (Array.isArray(exported)) return exported[0];
23
+ return exported;
24
+ }
25
+
26
+ /** Merge user input options onto a framework base (plugins appended, resolve/external merged). */
27
+ export function mergeInputOptions(base: InputOptions, user?: RolldownOptions): InputOptions {
28
+ if (!user) return base;
29
+ const {
30
+ output: _,
31
+ plugins: userPlugins,
32
+ resolve: userResolve,
33
+ external: userExternal,
34
+ ...rest
35
+ } = user;
36
+ return {
37
+ ...base,
38
+ ...rest,
39
+ plugins: [...toArray(base.plugins), ...toArray(userPlugins)] as RolldownPluginOption[],
40
+ resolve: {
41
+ ...base.resolve,
42
+ ...userResolve,
43
+ alias: {
44
+ ...(base.resolve?.alias as Record<string, string>),
45
+ ...(userResolve?.alias as Record<string, string>),
46
+ },
47
+ },
48
+ external: [...toArray(base.external), ...toArray(userExternal)] as ExternalOption,
49
+ };
50
+ }
51
+
52
+ /** Merge user output options onto a framework base. */
53
+ export function mergeOutputOptions(base: OutputOptions, user?: RolldownOptions): OutputOptions {
54
+ if (!user?.output) return base;
55
+ const out = Array.isArray(user.output) ? user.output[0] : user.output;
56
+ return { ...base, ...out };
57
+ }
58
+
59
+ function toArray<T>(value: T | T[] | undefined | null): T[] {
60
+ if (value == null) return [];
61
+ return Array.isArray(value) ? value : [value];
62
+ }
63
+
64
+ /** Raw text replacement plugin for `%KEY%` patterns (like Vite's html env replacement). */
65
+ export function htmlReplacePlugin(replacements: Record<string, string>): Plugin {
66
+ const entries = Object.entries(replacements);
67
+ if (entries.length === 0) return { name: "html-replace" };
68
+ return {
69
+ name: "html-replace",
70
+ transform(code) {
71
+ let result = code;
72
+ for (const [key, value] of entries) {
73
+ result = result.replaceAll(key, value);
74
+ }
75
+ if (result === code) return null;
76
+ return { code: result };
77
+ },
78
+ };
79
+ }
2
80
 
3
81
  export const assetUrlPrefixPlugin = {
4
82
  name: "asset-url-prefix",
package/src/build.ts CHANGED
@@ -7,6 +7,7 @@ import { argv, env } from "node:process";
7
7
  import { parseArgs } from "node:util";
8
8
  import { buildClient } from "./build.bundle-client.ts";
9
9
  import { buildServer } from "./build.bundle-server.ts";
10
+ import { loadUserConfig } from "./build.bundle.ts";
10
11
  import { exists } from "./fs.ts";
11
12
 
12
13
  /** Resolve paths relative to the current working directory. */
@@ -110,6 +111,9 @@ async function build() {
110
111
 
111
112
  await scaffoldTemplates();
112
113
 
114
+ const userConfig = await loadUserConfig(ROOT_DIR);
115
+ if (userConfig) console.log("📋 Loaded rolldown.config.ts");
116
+
113
117
  await buildClient({
114
118
  args,
115
119
  rootDir: ROOT_DIR,
@@ -118,6 +122,7 @@ async function build() {
118
122
  distClient: DIST_CLIENT,
119
123
  publicDir: PUBLIC_DIR,
120
124
  chunksPath: CHUNKS_PATH,
125
+ userConfig,
121
126
  });
122
127
 
123
128
  await buildServer({
@@ -128,6 +133,7 @@ async function build() {
128
133
  modulesPath: MODULES_PATH,
129
134
  chunksPath: CHUNKS_PATH,
130
135
  routesTypePath: ROUTES_TYPE_PATH,
136
+ userConfig,
131
137
  });
132
138
 
133
139
  const duration = ((performance.now() - startTime) / 1000).toFixed(2);
@@ -5,13 +5,12 @@ interface ImportMeta {
5
5
  ): Record<string, () => Promise<T>>;
6
6
  /** The file path of the current module (Node runtime) */
7
7
  path?: string;
8
- /** Environment variables (bundler) */
9
- env?: {
8
+ /** Environment variables replaced at build time via `transform.define` in `rolldown.config.ts`. */
9
+ env: {
10
10
  DEV?: boolean;
11
11
  PROD?: boolean;
12
12
  MODE?: string;
13
- [key: string]: unknown;
14
- };
13
+ } & Record<string, string>;
15
14
  }
16
15
 
17
16
  declare module "*.css" {