@blinkk/root 1.0.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/bin/root.js ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {Command} from 'commander';
4
+ import {build, dev, start} from '../dist/cli.js';
5
+
6
+ const program = new Command('root');
7
+ program.version(process.env.npm_package_version || 'dev');
8
+
9
+ program
10
+ .command('build [path]')
11
+ .description('generates a static build')
12
+ .action(build);
13
+ program
14
+ .command('dev [path]')
15
+ .description('starts the server in development mode')
16
+ .action(dev);
17
+ program
18
+ .command('start [path]')
19
+ .description('starts the server in production mode')
20
+ .action(start);
21
+
22
+ program.parse(process.argv);
@@ -0,0 +1,93 @@
1
+ // src/core/components/error-page.tsx
2
+ import { h } from "preact";
3
+ function ErrorPage(props) {
4
+ const error = props.error;
5
+ let message = void 0;
6
+ if (import.meta.env.DEV) {
7
+ if (error instanceof Error) {
8
+ message = error.stack;
9
+ } else {
10
+ message = String(error);
11
+ }
12
+ }
13
+ return /* @__PURE__ */ h("div", null, /* @__PURE__ */ h("div", null, /* @__PURE__ */ h("p", null, "An error occured during route handling or page rendering."), message && /* @__PURE__ */ h("pre", null, message)));
14
+ }
15
+
16
+ // src/core/components/head.ts
17
+ import { createContext } from "preact";
18
+ import { useContext } from "preact/hooks";
19
+ var HEAD_CONTEXT = createContext([]);
20
+ function Head(props) {
21
+ let context;
22
+ try {
23
+ context = useContext(HEAD_CONTEXT);
24
+ } catch (err) {
25
+ console.log(err);
26
+ throw new Error("<Head> component is not supported in the browser, or during suspense renders.", { cause: err });
27
+ }
28
+ context.push(props.children);
29
+ return null;
30
+ }
31
+
32
+ // src/core/components/script.ts
33
+ import { createContext as createContext2 } from "preact";
34
+ import { useContext as useContext2 } from "preact/hooks";
35
+ var SCRIPT_CONTEXT = createContext2([]);
36
+ function Script(props) {
37
+ let context;
38
+ try {
39
+ context = useContext2(SCRIPT_CONTEXT);
40
+ } catch (err) {
41
+ throw new Error("<Script> component is not supported in the browser, or during suspense renders.", { cause: err });
42
+ }
43
+ context.push(props);
44
+ return null;
45
+ }
46
+
47
+ // src/core/i18n.ts
48
+ import path from "node:path";
49
+ import { createContext as createContext3 } from "preact";
50
+ import { useContext as useContext3 } from "preact/hooks";
51
+ var I18N_CONTEXT = createContext3(null);
52
+ function t(str, params) {
53
+ const context = useContext3(I18N_CONTEXT);
54
+ if (!context) {
55
+ throw new Error("could not find i18n context");
56
+ }
57
+ const translations = (context == null ? void 0 : context.translations) || {};
58
+ let translation = translations[str] || str;
59
+ if (params) {
60
+ for (const key in params) {
61
+ const val = String(params[key] || "");
62
+ translation = translation.replaceAll(`{${key}}`, val);
63
+ }
64
+ }
65
+ return translation;
66
+ }
67
+ function getTranslations(locale) {
68
+ const translations = {};
69
+ const translationsFiles = import.meta.glob(["/translations/*.json"], {
70
+ eager: true
71
+ });
72
+ Object.keys(translationsFiles).forEach((translationPath) => {
73
+ const parts = path.parse(translationPath);
74
+ const locale2 = parts.name;
75
+ const module = translationsFiles[translationPath];
76
+ if (module && module.default) {
77
+ translations[locale2] = module.default;
78
+ }
79
+ });
80
+ return translations[locale] || {};
81
+ }
82
+
83
+ export {
84
+ ErrorPage,
85
+ HEAD_CONTEXT,
86
+ Head,
87
+ SCRIPT_CONTEXT,
88
+ Script,
89
+ I18N_CONTEXT,
90
+ t,
91
+ getTranslations
92
+ };
93
+ //# sourceMappingURL=chunk-4SVK7R4Z.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/components/error-page.tsx","../src/core/components/head.ts","../src/core/components/script.ts","../src/core/i18n.ts"],"sourcesContent":["import {h} from 'preact';\n\nexport interface ErrorPageProps {\n error: unknown;\n}\n\nexport function ErrorPage(props: ErrorPageProps) {\n const error = props.error;\n\n let message = undefined;\n if (import.meta.env.DEV) {\n if (error instanceof Error) {\n message = error.stack;\n } else {\n message = String(error);\n }\n }\n\n return (\n <div>\n <div>\n <p>An error occured during route handling or page rendering.</p>\n {message && <pre>{message}</pre>}\n </div>\n </div>\n );\n}\n","import {ComponentChildren, createContext} from 'preact';\nimport {useContext} from 'preact/hooks';\n\nexport const HEAD_CONTEXT = createContext<ComponentChildren[]>([]);\n\nexport interface HeadProps {\n children?: ComponentChildren;\n}\n\n/**\n * The <Head> component can be used for injecting elements into the HTML head\n * tag from any part of a page.\n */\nexport function Head(props: HeadProps) {\n let context: ComponentChildren[];\n try {\n context = useContext(HEAD_CONTEXT);\n } catch (err) {\n console.log(err);\n throw new Error(\n '<Head> component is not supported in the browser, or during suspense renders.',\n {cause: err}\n );\n }\n context.push(props.children);\n return null;\n}\n","import {createContext} from 'preact';\nimport {useContext} from 'preact/hooks';\n\nexport const SCRIPT_CONTEXT = createContext<ScriptProps[]>([]);\n\nexport interface ScriptProps {\n src: string;\n type?: string;\n}\n\n/**\n * The <Script> component is used for rendering any custom script modules. At\n * the moment, the system only pre-renders and bundles files that are in the\n * `src/bundles` folder at the root of the project.\n */\nexport function Script(props: ScriptProps) {\n let context: ScriptProps[];\n try {\n context = useContext(SCRIPT_CONTEXT);\n } catch (err) {\n throw new Error(\n '<Script> component is not supported in the browser, or during suspense renders.',\n {cause: err}\n );\n }\n context.push(props);\n return null;\n}\n","import path from 'node:path';\nimport {createContext} from 'preact';\nimport {useContext} from 'preact/hooks';\n\nexport const I18N_CONTEXT = createContext<I18nContext | null>(null);\n\nexport interface I18nContext {\n locale: string;\n translations: Record<string, string>;\n}\n\nexport function t(str: string, params?: Record<string, string>) {\n const context = useContext(I18N_CONTEXT);\n if (!context) {\n throw new Error('could not find i18n context');\n }\n const translations = context?.translations || {};\n let translation = translations[str] || str;\n if (params) {\n for (const key in params) {\n const val = String(params[key] || '');\n translation = translation.replaceAll(`{${key}}`, val);\n }\n }\n return translation;\n}\n\nexport function getTranslations(locale: string): Record<string, string> {\n const translations: Record<string, Record<string, string>> = {};\n const translationsFiles = import.meta.glob(['/translations/*.json'], {\n eager: true,\n }) as Record<string, {default?: Record<string, string>}>;\n Object.keys(translationsFiles).forEach((translationPath) => {\n const parts = path.parse(translationPath);\n const locale = parts.name;\n const module = translationsFiles[translationPath];\n if (module && module.default) {\n translations[locale] = module.default;\n }\n });\n return translations[locale] || {};\n}\n"],"mappings":";AAAA;AAMO,mBAAmB,OAAuB;AAC/C,QAAM,QAAQ,MAAM;AAEpB,MAAI,UAAU;AACd,MAAI,YAAY,IAAI,KAAK;AACvB,QAAI,iBAAiB,OAAO;AAC1B,gBAAU,MAAM;AAAA,IAClB,OAAO;AACL,gBAAU,OAAO,KAAK;AAAA,IACxB;AAAA,EACF;AAEA,SACE,kBAAC,aACC,kBAAC,aACC,kBAAC,WAAE,2DAAyD,GAC3D,WAAW,kBAAC,aAAK,OAAQ,CAC5B,CACF;AAEJ;;;AC1BA;AACA;AAEO,IAAM,eAAe,cAAmC,CAAC,CAAC;AAU1D,cAAc,OAAkB;AACrC,MAAI;AACJ,MAAI;AACF,cAAU,WAAW,YAAY;AAAA,EACnC,SAAS,KAAP;AACA,YAAQ,IAAI,GAAG;AACf,UAAM,IAAI,MACR,iFACA,EAAC,OAAO,IAAG,CACb;AAAA,EACF;AACA,UAAQ,KAAK,MAAM,QAAQ;AAC3B,SAAO;AACT;;;AC1BA;AACA;AAEO,IAAM,iBAAiB,eAA6B,CAAC,CAAC;AAYtD,gBAAgB,OAAoB;AACzC,MAAI;AACJ,MAAI;AACF,cAAU,YAAW,cAAc;AAAA,EACrC,SAAS,KAAP;AACA,UAAM,IAAI,MACR,mFACA,EAAC,OAAO,IAAG,CACb;AAAA,EACF;AACA,UAAQ,KAAK,KAAK;AAClB,SAAO;AACT;;;AC3BA;AACA;AACA;AAEO,IAAM,eAAe,eAAkC,IAAI;AAO3D,WAAW,KAAa,QAAiC;AAC9D,QAAM,UAAU,YAAW,YAAY;AACvC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,6BAA6B;AAAA,EAC/C;AACA,QAAM,eAAe,oCAAS,iBAAgB,CAAC;AAC/C,MAAI,cAAc,aAAa,QAAQ;AACvC,MAAI,QAAQ;AACV,eAAW,OAAO,QAAQ;AACxB,YAAM,MAAM,OAAO,OAAO,QAAQ,EAAE;AACpC,oBAAc,YAAY,WAAW,IAAI,QAAQ,GAAG;AAAA,IACtD;AAAA,EACF;AACA,SAAO;AACT;AAEO,yBAAyB,QAAwC;AACtE,QAAM,eAAuD,CAAC;AAC9D,QAAM,oBAAoB,YAAY,KAAK,CAAC,sBAAsB,GAAG;AAAA,IACnE,OAAO;AAAA,EACT,CAAC;AACD,SAAO,KAAK,iBAAiB,EAAE,QAAQ,CAAC,oBAAoB;AAC1D,UAAM,QAAQ,KAAK,MAAM,eAAe;AACxC,UAAM,UAAS,MAAM;AACrB,UAAM,SAAS,kBAAkB;AACjC,QAAI,UAAU,OAAO,SAAS;AAC5B,mBAAa,WAAU,OAAO;AAAA,IAChC;AAAA,EACF,CAAC;AACD,SAAO,aAAa,WAAW,CAAC;AAClC;","names":[]}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ declare function dev(rootDir?: string): void;
2
+
3
+ declare function build(rootDir?: string): Promise<void>;
4
+
5
+ declare function start(rootDir?: string): Promise<void>;
6
+
7
+ export { build, dev, start };
package/dist/cli.js ADDED
@@ -0,0 +1,548 @@
1
+ // src/cli/commands/dev.ts
2
+ import path3 from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { default as express } from "express";
5
+ import { createServer as createViteServer } from "vite";
6
+
7
+ // src/render/vite-plugin-root.ts
8
+ import path from "path";
9
+ import glob from "tiny-glob";
10
+ var ELEMENTS_REGEX = /h\("(\w[\w-]+\w)"/g;
11
+ function pluginRoot(options) {
12
+ const rootDir = (options == null ? void 0 : options.rootDir) || process.cwd();
13
+ let config;
14
+ let server;
15
+ let elementMap;
16
+ async function updateElementMap() {
17
+ elementMap = {};
18
+ const files = await glob("./elements/**/*", { cwd: rootDir });
19
+ files.forEach((file) => {
20
+ const parts = path.parse(file);
21
+ if (isJsFile(parts.base)) {
22
+ elementMap[parts.name] = `${rootDir}/${file}`;
23
+ }
24
+ });
25
+ }
26
+ async function getElementImport(tagName) {
27
+ if (!elementMap) {
28
+ await updateElementMap();
29
+ }
30
+ if (tagName in elementMap) {
31
+ const filePath = elementMap[tagName];
32
+ if (server && server.moduleGraph) {
33
+ const moduleSet = server.moduleGraph.getModulesByFile(filePath);
34
+ if (moduleSet && moduleSet.size > 0) {
35
+ const module = moduleSet.values().next().value;
36
+ return module.url;
37
+ }
38
+ }
39
+ }
40
+ return null;
41
+ }
42
+ return {
43
+ name: "vite-plugin-root",
44
+ configResolved(resolvedConfig) {
45
+ config = resolvedConfig;
46
+ },
47
+ configureServer(_server) {
48
+ server = _server;
49
+ },
50
+ async transform(src, id) {
51
+ if (id.includes("/elements/") && isJsFile(id)) {
52
+ const idParts = path.parse(id);
53
+ const deps = /* @__PURE__ */ new Set();
54
+ const elementsRe = ELEMENTS_REGEX;
55
+ for (const match of src.matchAll(elementsRe)) {
56
+ const tagName = match[1];
57
+ if (!tagName.includes("-")) {
58
+ continue;
59
+ }
60
+ if (tagName === idParts.name) {
61
+ continue;
62
+ }
63
+ deps.add(tagName);
64
+ }
65
+ const importUrls = [];
66
+ await Promise.all(Array.from(deps).map(async (tagName) => {
67
+ const importUrl = await getElementImport(tagName);
68
+ if (importUrl) {
69
+ importUrls.push(importUrl);
70
+ }
71
+ }));
72
+ if (importUrls.length > 0) {
73
+ const importLines = importUrls.map((url) => `import '${url}';`).join("\n");
74
+ const code = `${src}
75
+
76
+ // autogenerated by root:
77
+ ${importLines}
78
+ `;
79
+ return { code };
80
+ }
81
+ return null;
82
+ }
83
+ }
84
+ };
85
+ }
86
+ function isJsFile(file) {
87
+ return !!file.match(/\.(j|t)sx?$/);
88
+ }
89
+ var vite_plugin_root_default = pluginRoot;
90
+
91
+ // src/render/asset-map/dev-asset-map.ts
92
+ import path2 from "node:path";
93
+ var DevServerAssetMap = class {
94
+ constructor(moduleGraph) {
95
+ this.moduleGraph = moduleGraph;
96
+ }
97
+ async get(moduleId) {
98
+ const viteModule = await this.moduleGraph.getModuleByUrl(moduleId);
99
+ if (!viteModule || !viteModule.id) {
100
+ return null;
101
+ }
102
+ return new DevServerAsset(this, viteModule);
103
+ }
104
+ };
105
+ var DevServerAsset = class {
106
+ constructor(assetMap, viteModule) {
107
+ this.assetMap = assetMap;
108
+ this.viteModule = viteModule;
109
+ this.moduleId = this.viteModule.id;
110
+ this.assetUrl = this.viteModule.url;
111
+ }
112
+ async getCssDeps() {
113
+ const visited = /* @__PURE__ */ new Set();
114
+ const deps = /* @__PURE__ */ new Set();
115
+ this.collectCss(this, deps, visited);
116
+ return Array.from(deps);
117
+ }
118
+ async getJsDeps() {
119
+ const visited = /* @__PURE__ */ new Set();
120
+ const deps = /* @__PURE__ */ new Set();
121
+ this.collectJs(this, deps, visited);
122
+ return Array.from(deps);
123
+ }
124
+ getImportedModules() {
125
+ return this.viteModule.importedModules;
126
+ }
127
+ collectJs(asset, urls, visited) {
128
+ if (!asset) {
129
+ return;
130
+ }
131
+ if (!asset.moduleId) {
132
+ return;
133
+ }
134
+ if (visited.has(asset.moduleId)) {
135
+ return;
136
+ }
137
+ visited.add(asset.moduleId);
138
+ const parts = path2.parse(asset.assetUrl);
139
+ if ([".js", ".jsx", ".ts", ".tsx"].includes(parts.ext) && asset.moduleId.includes("/elements/")) {
140
+ urls.add(asset.assetUrl);
141
+ }
142
+ asset.getImportedModules().forEach((viteModule) => {
143
+ if (viteModule.id) {
144
+ const importedAsset = new DevServerAsset(this.assetMap, viteModule);
145
+ this.collectJs(importedAsset, urls, visited);
146
+ }
147
+ });
148
+ }
149
+ collectCss(asset, urls, visited) {
150
+ if (!asset) {
151
+ return;
152
+ }
153
+ if (!asset.assetUrl) {
154
+ return;
155
+ }
156
+ if (visited.has(asset.assetUrl)) {
157
+ return;
158
+ }
159
+ visited.add(asset.assetUrl);
160
+ if (asset.moduleId.endsWith(".scss")) {
161
+ const parts = path2.parse(asset.assetUrl);
162
+ if (!parts.name.startsWith("_")) {
163
+ urls.add(asset.assetUrl);
164
+ }
165
+ }
166
+ asset.getImportedModules().forEach((viteModule) => {
167
+ if (viteModule.id) {
168
+ const importedAsset = new DevServerAsset(this.assetMap, viteModule);
169
+ this.collectCss(importedAsset, urls, visited);
170
+ }
171
+ });
172
+ }
173
+ };
174
+
175
+ // src/cli/load-config.ts
176
+ import { bundleRequire } from "bundle-require";
177
+ import JoyCon from "joycon";
178
+ async function loadRootConfig(rootDir) {
179
+ const joycon = new JoyCon();
180
+ const configPath = await joycon.resolve({
181
+ cwd: rootDir,
182
+ files: ["root.config.ts"]
183
+ });
184
+ if (configPath) {
185
+ const configBundle = await bundleRequire({
186
+ filepath: configPath
187
+ });
188
+ return configBundle.mod.default || {};
189
+ }
190
+ return {};
191
+ }
192
+
193
+ // src/render/html-minify.ts
194
+ import { minify } from "html-minifier-terser";
195
+ async function htmlMinify(html) {
196
+ const min = await minify(html, {
197
+ collapseWhitespace: true,
198
+ removeComments: true,
199
+ preserveLineBreaks: true
200
+ });
201
+ return min.trimStart();
202
+ }
203
+
204
+ // src/cli/commands/dev.ts
205
+ var __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
206
+ async function createServer(options) {
207
+ const app = express();
208
+ app.use(express.static("public"));
209
+ const middlewares = await getMiddlewares({ rootDir: options == null ? void 0 : options.rootDir });
210
+ middlewares.forEach((middleware) => app.use(middleware));
211
+ const port = parseInt(process.env.PORT || "4007");
212
+ const version = process.env.npm_package_version || "dev";
213
+ console.log(`\u{1F333} Root.js v${version}`);
214
+ console.log();
215
+ console.log(`Started server: http://localhost:${port}`);
216
+ app.listen(port);
217
+ }
218
+ async function getMiddlewares(options) {
219
+ const rootDir = (options == null ? void 0 : options.rootDir) || process.cwd();
220
+ const rootConfig = await loadRootConfig(rootDir);
221
+ const viteConfig = rootConfig.vite || {};
222
+ const renderModulePath = path3.resolve(__dirname2, "./render.js");
223
+ const viteServer = await createViteServer({
224
+ ...viteConfig,
225
+ server: { middlewareMode: true },
226
+ appType: "custom",
227
+ optimizeDeps: {
228
+ include: [renderModulePath]
229
+ },
230
+ esbuild: {
231
+ jsx: "automatic",
232
+ jsxImportSource: "preact"
233
+ },
234
+ plugins: [...viteConfig.plugins || [], vite_plugin_root_default()]
235
+ });
236
+ const rootMiddleware = async (req, res, next) => {
237
+ const url = req.originalUrl;
238
+ const render = await viteServer.ssrLoadModule(renderModulePath);
239
+ const renderer = new render.Renderer(rootConfig);
240
+ try {
241
+ const assetMap = new DevServerAssetMap(viteServer.moduleGraph);
242
+ const data = await renderer.render(url, {
243
+ assetMap
244
+ });
245
+ const html = await htmlMinify(await viteServer.transformIndexHtml(url, data.html || ""));
246
+ res.status(200).set({ "Content-Type": "text/html" }).end(html);
247
+ } catch (e) {
248
+ viteServer.ssrFixStacktrace(e);
249
+ try {
250
+ const { html } = await renderer.renderError(e);
251
+ res.status(500).set({ "Content-Type": "text/html" }).end(html);
252
+ } catch (e2) {
253
+ console.error("failed to render custom error");
254
+ console.error(e2);
255
+ next(e);
256
+ }
257
+ }
258
+ };
259
+ return [viteServer.middlewares, rootMiddleware];
260
+ }
261
+ function dev(rootDir) {
262
+ createServer({ rootDir });
263
+ }
264
+
265
+ // src/cli/commands/build.ts
266
+ import path6 from "node:path";
267
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
268
+ import fsExtra2 from "fs-extra";
269
+ import glob2 from "tiny-glob";
270
+ import { build as viteBuild } from "vite";
271
+
272
+ // src/render/asset-map/build-asset-map.ts
273
+ import path4 from "path";
274
+ var BuildAssetMap = class {
275
+ constructor(manifest) {
276
+ this.manifest = manifest;
277
+ this.moduleIdToAsset = /* @__PURE__ */ new Map();
278
+ Object.keys(this.manifest).forEach((manifestKey) => {
279
+ const moduleId = `/${manifestKey}`;
280
+ this.moduleIdToAsset.set(moduleId, new BuildAsset(this, moduleId, this.manifest[manifestKey]));
281
+ });
282
+ }
283
+ async get(moduleId) {
284
+ return this.moduleIdToAsset.get(moduleId) || null;
285
+ }
286
+ toJson() {
287
+ const result = {};
288
+ for (const moduleId of this.moduleIdToAsset.keys()) {
289
+ result[moduleId] = this.moduleIdToAsset.get(moduleId).toJson();
290
+ }
291
+ return result;
292
+ }
293
+ };
294
+ var BuildAsset = class {
295
+ constructor(assetMap, moduleId, manifestData) {
296
+ this.assetMap = assetMap;
297
+ this.moduleId = moduleId;
298
+ this.manifestData = manifestData;
299
+ this.assetUrl = `/${manifestData.file}`;
300
+ this.importedModules = (this.manifestData.imports || []).map((relPath) => `/${relPath}`);
301
+ this.importedCss = (this.manifestData.css || []).map((relPath) => `/${relPath}`);
302
+ }
303
+ async getCssDeps() {
304
+ const visited = /* @__PURE__ */ new Set();
305
+ const deps = /* @__PURE__ */ new Set();
306
+ await this.collectCss(this, deps, visited);
307
+ return Array.from(deps);
308
+ }
309
+ async getJsDeps() {
310
+ const visited = /* @__PURE__ */ new Set();
311
+ const deps = /* @__PURE__ */ new Set();
312
+ await this.collectJs(this, deps, visited);
313
+ return Array.from(deps);
314
+ }
315
+ async collectJs(asset, urls, visited) {
316
+ if (!asset) {
317
+ return;
318
+ }
319
+ if (!asset.moduleId) {
320
+ return;
321
+ }
322
+ if (visited.has(asset.moduleId)) {
323
+ return;
324
+ }
325
+ visited.add(asset.moduleId);
326
+ const parts = path4.parse(asset.assetUrl);
327
+ if ([".ts", ".tsx"].includes(parts.ext) && asset.moduleId.includes("/elements/")) {
328
+ urls.add(asset.assetUrl);
329
+ }
330
+ await Promise.all(asset.importedModules.map(async (moduleId) => {
331
+ const importedAsset = await this.assetMap.get(moduleId);
332
+ this.collectJs(importedAsset, urls, visited);
333
+ }));
334
+ }
335
+ async collectCss(asset, urls, visited) {
336
+ if (!asset) {
337
+ return;
338
+ }
339
+ if (!asset.assetUrl) {
340
+ return;
341
+ }
342
+ if (visited.has(asset.moduleId)) {
343
+ return;
344
+ }
345
+ visited.add(asset.moduleId);
346
+ if (asset.importedCss) {
347
+ asset.importedCss.forEach((cssUrl) => urls.add(cssUrl));
348
+ }
349
+ await Promise.all(asset.importedModules.map(async (moduleId) => {
350
+ const importedAsset = await this.assetMap.get(moduleId);
351
+ this.collectCss(importedAsset, urls, visited);
352
+ }));
353
+ }
354
+ toJson() {
355
+ return {
356
+ moduleId: this.moduleId,
357
+ assetUrl: this.assetUrl,
358
+ importedModules: this.importedModules,
359
+ importedCss: this.importedCss
360
+ };
361
+ }
362
+ };
363
+
364
+ // src/core/fsutils.ts
365
+ import { promises as fs } from "node:fs";
366
+ import path5 from "node:path";
367
+ import fsExtra from "fs-extra";
368
+ function isJsFile2(file) {
369
+ return !!file.match(/\.(j|t)sx?$/);
370
+ }
371
+ async function writeFile(filePath, content) {
372
+ const dirPath = path5.dirname(filePath);
373
+ await makeDir(dirPath);
374
+ await fs.writeFile(filePath, content);
375
+ }
376
+ async function makeDir(dirPath) {
377
+ try {
378
+ await fs.access(dirPath);
379
+ } catch (e) {
380
+ await fs.mkdir(dirPath, { recursive: true });
381
+ }
382
+ }
383
+ async function copyDir(srcDir, dstDir) {
384
+ if (!fsExtra.existsSync(srcDir)) {
385
+ return;
386
+ }
387
+ fsExtra.copySync(srcDir, dstDir, { recursive: true, overwrite: true });
388
+ }
389
+ async function rmDir(dirPath) {
390
+ await fs.rm(dirPath, { recursive: true, force: true });
391
+ }
392
+ async function loadJson(filePath) {
393
+ const content = await fs.readFile(filePath, "utf-8");
394
+ return JSON.parse(content);
395
+ }
396
+ async function isDirectory(dirPath) {
397
+ return fs.stat(dirPath).then((fsStat) => {
398
+ return fsStat.isDirectory();
399
+ }).catch((err) => {
400
+ if (err.code === "ENOENT") {
401
+ return false;
402
+ }
403
+ throw err;
404
+ });
405
+ }
406
+
407
+ // src/cli/commands/build.ts
408
+ var __dirname3 = path6.dirname(fileURLToPath2(import.meta.url));
409
+ async function build(rootDir) {
410
+ const version = process.env.npm_package_version || "dev";
411
+ console.log(`\u{1F333} Root.js v${version}`);
412
+ if (!rootDir) {
413
+ rootDir = process.cwd();
414
+ }
415
+ const rootConfig = await loadRootConfig(rootDir);
416
+ const distDir = path6.join(rootDir, "dist");
417
+ await rmDir(distDir);
418
+ await makeDir(distDir);
419
+ const pages = [];
420
+ if (await isDirectory(path6.join(rootDir, "routes"))) {
421
+ const pageFiles = await glob2(path6.join(rootDir, "routes/**/*"));
422
+ pageFiles.forEach((file) => {
423
+ const parts = path6.parse(file);
424
+ if (!parts.name.startsWith("_") && isJsFile2(parts.base)) {
425
+ pages.push(file);
426
+ }
427
+ });
428
+ }
429
+ const elements = [];
430
+ if (await isDirectory(path6.join(rootDir, "elements"))) {
431
+ const elementFiles = await glob2(path6.join(rootDir, "elements/**/*"));
432
+ elementFiles.forEach((file) => {
433
+ const parts = path6.parse(file);
434
+ if (isJsFile2(parts.base)) {
435
+ elements.push(file);
436
+ }
437
+ });
438
+ }
439
+ const bundleScripts = [];
440
+ if (await isDirectory(path6.join(rootDir, "bundles"))) {
441
+ const bundleFiles = await glob2(path6.join(rootDir, "bundles/*"));
442
+ bundleFiles.forEach((file) => {
443
+ const parts = path6.parse(file);
444
+ if (isJsFile2(parts.base)) {
445
+ bundleScripts.push(file);
446
+ }
447
+ });
448
+ }
449
+ const viteConfig = rootConfig.vite || {};
450
+ const baseConfig = {
451
+ ...viteConfig,
452
+ root: rootDir,
453
+ esbuild: {
454
+ jsxFactory: "h",
455
+ jsxFragment: "Fragment",
456
+ jsxInject: 'import {h, Fragment} from "preact";'
457
+ },
458
+ plugins: [...viteConfig.plugins || [], pluginRoot()]
459
+ };
460
+ await viteBuild({
461
+ ...baseConfig,
462
+ mode: "production",
463
+ publicDir: false,
464
+ build: {
465
+ rollupOptions: {
466
+ input: [path6.resolve(__dirname3, "./render.js")],
467
+ output: {
468
+ format: "esm",
469
+ chunkFileNames: "chunks/[name].[hash].js",
470
+ assetFileNames: "assets/[name].[hash][extname]"
471
+ }
472
+ },
473
+ outDir: path6.join(distDir, "server"),
474
+ ssr: true,
475
+ ssrManifest: false,
476
+ cssCodeSplit: true,
477
+ target: "esnext",
478
+ minify: false,
479
+ polyfillModulePreload: false,
480
+ reportCompressedSize: false
481
+ }
482
+ });
483
+ await viteBuild({
484
+ ...baseConfig,
485
+ mode: "production",
486
+ publicDir: false,
487
+ build: {
488
+ rollupOptions: {
489
+ input: [...pages, ...elements, ...bundleScripts],
490
+ output: {
491
+ format: "esm",
492
+ chunkFileNames: "chunks/[name].[hash].js",
493
+ assetFileNames: "assets/[name].[hash][extname]"
494
+ }
495
+ },
496
+ outDir: path6.join(distDir, "client"),
497
+ ssr: false,
498
+ ssrManifest: false,
499
+ manifest: true,
500
+ cssCodeSplit: true,
501
+ target: "esnext",
502
+ minify: true,
503
+ polyfillModulePreload: false,
504
+ reportCompressedSize: false
505
+ }
506
+ });
507
+ const manifest = await loadJson(path6.join(distDir, "client/manifest.json"));
508
+ const assetMap = new BuildAssetMap(manifest);
509
+ writeFile(path6.join(distDir, "client/root-manifest.json"), JSON.stringify(assetMap.toJson(), null, 2));
510
+ const buildDir = path6.join(distDir, "html");
511
+ const publicDir = path6.join(rootDir, "public");
512
+ if (fsExtra2.existsSync(path6.join(rootDir, "public"))) {
513
+ fsExtra2.copySync(publicDir, buildDir);
514
+ } else {
515
+ makeDir(buildDir);
516
+ }
517
+ copyDir(path6.join(distDir, "client/assets"), path6.join(buildDir, "assets"));
518
+ copyDir(path6.join(distDir, "client/chunks"), path6.join(buildDir, "chunks"));
519
+ const render = await import(path6.join(distDir, "server/render.js"));
520
+ const renderer = new render.Renderer(rootConfig);
521
+ const sitemap = await renderer.getSitemap();
522
+ await Promise.all(Object.keys(sitemap).map(async (urlPath) => {
523
+ const { route, params } = sitemap[urlPath];
524
+ const data = await renderer.renderRoute(route, {
525
+ assetMap,
526
+ routeParams: params
527
+ });
528
+ let outPath = path6.join(distDir, `html${urlPath}`, "index.html");
529
+ if (outPath.endsWith("404/index.html")) {
530
+ outPath = outPath.replace("404/index.html", "404.html");
531
+ }
532
+ const html = await htmlMinify(data.html || "");
533
+ await writeFile(outPath, html);
534
+ const relPath = outPath.slice(path6.dirname(distDir).length + 1);
535
+ console.log(`saved ${relPath}`);
536
+ }));
537
+ }
538
+
539
+ // src/cli/commands/start.ts
540
+ async function start(rootDir) {
541
+ console.log("SSR production server is coming soon");
542
+ }
543
+ export {
544
+ build,
545
+ dev,
546
+ start
547
+ };
548
+ //# sourceMappingURL=cli.js.map