@blinkk/root 1.0.0-alpha.3 → 1.0.0-alpha.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/bin/root.js CHANGED
@@ -10,20 +10,24 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
10
  const require = createRequire(import.meta.url);
11
11
  const packageJson = require(path.join(__dirname, '../package.json'));
12
12
 
13
- const program = new Command('root');
14
- program.version(packageJson.version);
13
+ async function main() {
14
+ const program = new Command('root');
15
+ program.version(packageJson.version);
15
16
 
16
- program
17
- .command('build [path]')
18
- .description('generates a static build')
19
- .action(build);
20
- program
21
- .command('dev [path]')
22
- .description('starts the server in development mode')
23
- .action(dev);
24
- program
25
- .command('start [path]')
26
- .description('starts the server in production mode')
27
- .action(start);
17
+ program
18
+ .command('build [path]')
19
+ .description('generates a static build')
20
+ .action(build);
21
+ program
22
+ .command('dev [path]')
23
+ .description('starts the server in development mode')
24
+ .action(dev);
25
+ program
26
+ .command('start [path]')
27
+ .description('starts the server in production mode')
28
+ .action(start);
28
29
 
29
- program.parse(process.argv);
30
+ await program.parseAsync(process.argv);
31
+ }
32
+
33
+ main();
@@ -1,5 +1,5 @@
1
1
  // src/core/components/error-page.tsx
2
- import { h } from "preact";
2
+ import { jsx, jsxs } from "preact/jsx-runtime";
3
3
  function ErrorPage(props) {
4
4
  const error = props.error;
5
5
  let message = void 0;
@@ -10,7 +10,18 @@ function ErrorPage(props) {
10
10
  message = String(error);
11
11
  }
12
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)));
13
+ return /* @__PURE__ */ jsx("div", {
14
+ children: /* @__PURE__ */ jsxs("div", {
15
+ children: [
16
+ /* @__PURE__ */ jsx("p", {
17
+ children: "An error occured during route handling or page rendering."
18
+ }),
19
+ message && /* @__PURE__ */ jsx("pre", {
20
+ children: message
21
+ })
22
+ ]
23
+ })
24
+ });
14
25
  }
15
26
 
16
27
  // src/core/components/head.ts
@@ -23,7 +34,10 @@ function Head(props) {
23
34
  context = useContext(HEAD_CONTEXT);
24
35
  } catch (err) {
25
36
  console.log(err);
26
- throw new Error("<Head> component is not supported in the browser, or during suspense renders.", { cause: err });
37
+ throw new Error(
38
+ "<Head> component is not supported in the browser, or during suspense renders.",
39
+ { cause: err }
40
+ );
27
41
  }
28
42
  context.push(props.children);
29
43
  return null;
@@ -38,7 +52,10 @@ function Script(props) {
38
52
  try {
39
53
  context = useContext2(SCRIPT_CONTEXT);
40
54
  } catch (err) {
41
- throw new Error("<Script> component is not supported in the browser, or during suspense renders.", { cause: err });
55
+ throw new Error(
56
+ "<Script> component is not supported in the browser, or during suspense renders.",
57
+ { cause: err }
58
+ );
42
59
  }
43
60
  context.push(props);
44
61
  return null;
@@ -93,4 +110,4 @@ export {
93
110
  getTranslations,
94
111
  useTranslations
95
112
  };
96
- //# sourceMappingURL=chunk-CDPH3RKE.js.map
113
+ //# sourceMappingURL=chunk-5FQTLGCQ.js.map
@@ -1 +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":["// eslint-disable-next-line @typescript-eslint/no-unused-vars\nimport {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 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\nexport function useTranslations() {\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 const t = (str: string, params?: Record<string, string>) => {\n let translation = translations[str] || str || '';\n if (params) {\n for (const key of Object.keys(params)) {\n const val = String(params[key] || '');\n translation = translation.replaceAll(`{${key}}`, val);\n }\n }\n return translation;\n };\n return t;\n}\n"],"mappings":";AACA;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;;;AC3BA;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,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;AAEO,2BAA2B;AAChC,QAAM,UAAU,YAAW,YAAY;AACvC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,6BAA6B;AAAA,EAC/C;AACA,QAAM,eAAe,oCAAS,iBAAgB,CAAC;AAC/C,QAAM,IAAI,CAAC,KAAa,WAAoC;AAC1D,QAAI,cAAc,aAAa,QAAQ,OAAO;AAC9C,QAAI,QAAQ;AACV,iBAAW,OAAO,OAAO,KAAK,MAAM,GAAG;AACrC,cAAM,MAAM,OAAO,OAAO,QAAQ,EAAE;AACpC,sBAAc,YAAY,WAAW,IAAI,QAAQ,GAAG;AAAA,MACtD;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;","names":[]}
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":["// eslint-disable-next-line @typescript-eslint/no-unused-vars\nimport {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 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\nexport function useTranslations() {\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 const t = (str: string, params?: Record<string, string>) => {\n let translation = translations[str] || str || '';\n if (params) {\n for (const key of Object.keys(params)) {\n const val = String(params[key] || '');\n translation = translation.replaceAll(`{${key}}`, val);\n }\n }\n return translation;\n };\n return t;\n}\n"],"mappings":";AAqBM,SACE,KADF;AAdC,SAAS,UAAU,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,oBAAC;AAAA,IACC,+BAAC;AAAA,MACC;AAAA,4BAAC;AAAA,UAAE;AAAA,SAAyD;AAAA,QAC3D,WAAW,oBAAC;AAAA,UAAK;AAAA,SAAQ;AAAA;AAAA,KAC5B;AAAA,GACF;AAEJ;;;AC3BA,SAA2B,qBAAoB;AAC/C,SAAQ,kBAAiB;AAElB,IAAM,eAAe,cAAmC,CAAC,CAAC;AAU1D,SAAS,KAAK,OAAkB;AACrC,MAAI;AACJ,MAAI;AACF,cAAU,WAAW,YAAY;AAAA,EACnC,SAAS,KAAP;AACA,YAAQ,IAAI,GAAG;AACf,UAAM,IAAI;AAAA,MACR;AAAA,MACA,EAAC,OAAO,IAAG;AAAA,IACb;AAAA,EACF;AACA,UAAQ,KAAK,MAAM,QAAQ;AAC3B,SAAO;AACT;;;AC1BA,SAAQ,iBAAAA,sBAAoB;AAC5B,SAAQ,cAAAC,mBAAiB;AAElB,IAAM,iBAAiBD,eAA6B,CAAC,CAAC;AAYtD,SAAS,OAAO,OAAoB;AACzC,MAAI;AACJ,MAAI;AACF,cAAUC,YAAW,cAAc;AAAA,EACrC,SAAS,KAAP;AACA,UAAM,IAAI;AAAA,MACR;AAAA,MACA,EAAC,OAAO,IAAG;AAAA,IACb;AAAA,EACF;AACA,UAAQ,KAAK,KAAK;AAClB,SAAO;AACT;;;AC3BA,OAAO,UAAU;AACjB,SAAQ,iBAAAC,sBAAoB;AAC5B,SAAQ,cAAAC,mBAAiB;AAElB,IAAM,eAAeD,eAAkC,IAAI;AAO3D,SAAS,gBAAgB,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,UAAME,UAAS,MAAM;AACrB,UAAM,SAAS,kBAAkB;AACjC,QAAI,UAAU,OAAO,SAAS;AAC5B,mBAAaA,WAAU,OAAO;AAAA,IAChC;AAAA,EACF,CAAC;AACD,SAAO,aAAa,WAAW,CAAC;AAClC;AAEO,SAAS,kBAAkB;AAChC,QAAM,UAAUD,YAAW,YAAY;AACvC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,6BAA6B;AAAA,EAC/C;AACA,QAAM,gBAAe,mCAAS,iBAAgB,CAAC;AAC/C,QAAM,IAAI,CAAC,KAAa,WAAoC;AAC1D,QAAI,cAAc,aAAa,QAAQ,OAAO;AAC9C,QAAI,QAAQ;AACV,iBAAW,OAAO,OAAO,KAAK,MAAM,GAAG;AACrC,cAAM,MAAM,OAAO,OAAO,QAAQ,EAAE;AACpC,sBAAc,YAAY,WAAW,IAAI,QAAQ,GAAG;AAAA,MACtD;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;","names":["createContext","useContext","createContext","useContext","locale"]}
package/dist/cli.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- declare function dev(rootDir?: string): void;
1
+ declare function dev(rootDir?: string): Promise<void>;
2
2
 
3
- declare function build(rootDir?: string): Promise<void>;
3
+ declare function build(rootProjectDir?: string): Promise<void>;
4
4
 
5
5
  declare function start(): Promise<void>;
6
6
 
package/dist/cli.js CHANGED
@@ -55,18 +55,42 @@ async function isDirectory(dirpath) {
55
55
  var JSX_ELEMENTS_REGEX = /jsxs?\("(\w[\w-]+\w)"/g;
56
56
  var HTML_ELEMENTS_REGEX = /<(\w[\w-]+\w)/g;
57
57
  function pluginRoot(options) {
58
+ var _a;
59
+ const elementsVirtualId = "virtual:root-elements";
60
+ const resolvedElementsVirtualId = "\0" + elementsVirtualId;
58
61
  const rootDir = (options == null ? void 0 : options.rootDir) || process.cwd();
62
+ const rootConfig = (options == null ? void 0 : options.rootConfig) || {};
63
+ const elementsDirs = [path2.join(rootDir, "elements")];
64
+ const includeDirs = ((_a = rootConfig.elements) == null ? void 0 : _a.include) || [];
65
+ includeDirs.forEach((dirPath) => {
66
+ const elementsDir = path2.resolve(rootDir, dirPath);
67
+ if (!elementsDir.startsWith(rootDir)) {
68
+ throw new Error(
69
+ `the elements dir (${dirPath}) should be relative to the project's root dir (${rootDir})`
70
+ );
71
+ }
72
+ elementsDirs.push(elementsDir);
73
+ });
59
74
  let elementMap;
60
75
  async function updateElementMap() {
61
76
  elementMap = {};
62
- const files = await glob("./elements/**/*", { cwd: rootDir });
63
- files.forEach((file) => {
64
- const parts = path2.parse(file);
65
- if (isJsFile(parts.base)) {
66
- elementMap[parts.name] = `/${file}`;
67
- }
68
- });
69
- console.log(JSON.stringify(elementMap));
77
+ await Promise.all(
78
+ elementsDirs.map(async (dirPath) => {
79
+ const dirExists = await isDirectory(dirPath);
80
+ if (!dirExists) {
81
+ return;
82
+ }
83
+ const files = await glob("**/*", { cwd: dirPath });
84
+ files.forEach((file) => {
85
+ const parts = path2.parse(file);
86
+ if (isJsFile(parts.base)) {
87
+ const fullPath = path2.join(dirPath, file);
88
+ const moduleId = fullPath.slice(rootDir.length);
89
+ elementMap[parts.name] = moduleId;
90
+ }
91
+ });
92
+ })
93
+ );
70
94
  }
71
95
  async function getElementImport(tagname) {
72
96
  if (!elementMap) {
@@ -77,14 +101,31 @@ function pluginRoot(options) {
77
101
  }
78
102
  return null;
79
103
  }
104
+ function isCustomElement(id) {
105
+ if (!isJsFile(id)) {
106
+ return false;
107
+ }
108
+ return elementsDirs.some((elementsDir) => {
109
+ return id.startsWith(elementsDir);
110
+ });
111
+ }
80
112
  return {
81
113
  name: "vite-plugin-root",
82
- async transform(src, id) {
83
- if (!id.startsWith(rootDir)) {
84
- return null;
114
+ resolveId(id) {
115
+ if (id === elementsVirtualId) {
116
+ return resolvedElementsVirtualId;
117
+ }
118
+ return null;
119
+ },
120
+ async load(id) {
121
+ if (id === resolvedElementsVirtualId) {
122
+ await updateElementMap();
123
+ return `export const elementsMap = ${JSON.stringify(elementMap)}`;
85
124
  }
86
- const moduleId = id.slice(rootDir.length);
87
- if (moduleId.startsWith("/elements/") && isJsFile(id)) {
125
+ return null;
126
+ },
127
+ async transform(src, id) {
128
+ if (isCustomElement(id)) {
88
129
  const idParts = path2.parse(id);
89
130
  const deps = /* @__PURE__ */ new Set();
90
131
  const tagnames = [
@@ -102,12 +143,14 @@ function pluginRoot(options) {
102
143
  deps.add(tagname);
103
144
  }
104
145
  const importUrls = [];
105
- await Promise.all(Array.from(deps).map(async (tagname) => {
106
- const importUrl = await getElementImport(tagname);
107
- if (importUrl) {
108
- importUrls.push(importUrl);
109
- }
110
- }));
146
+ await Promise.all(
147
+ Array.from(deps).map(async (tagname) => {
148
+ const importUrl = await getElementImport(tagname);
149
+ if (importUrl) {
150
+ importUrls.push(importUrl);
151
+ }
152
+ })
153
+ );
111
154
  if (importUrls.length > 0) {
112
155
  const importLines = importUrls.map((url) => `import '${url}';`).join("\n");
113
156
  const code = `${src}
@@ -115,11 +158,11 @@ function pluginRoot(options) {
115
158
  // autogenerated by root:
116
159
  ${importLines}
117
160
  `;
118
- console.log(code);
119
161
  return { code };
120
162
  }
121
163
  return null;
122
164
  }
165
+ return null;
123
166
  }
124
167
  };
125
168
  }
@@ -239,7 +282,7 @@ async function htmlMinify(html) {
239
282
 
240
283
  // src/cli/commands/dev.ts
241
284
  import glob2 from "tiny-glob";
242
- var __dirname2 = path4.dirname(fileURLToPath(import.meta.url));
285
+ var __dirname = path4.dirname(fileURLToPath(import.meta.url));
243
286
  async function createServer(options) {
244
287
  const app = express();
245
288
  app.use(express.static("public"));
@@ -248,14 +291,14 @@ async function createServer(options) {
248
291
  const port = parseInt(process.env.PORT || "4007");
249
292
  console.log("\u{1F333} Root.js");
250
293
  console.log();
251
- console.log(`Started server: http://localhost:${port}`);
294
+ console.log(`Started dev server: http://localhost:${port}`);
252
295
  app.listen(port);
253
296
  }
254
297
  async function getMiddlewares(options) {
255
298
  const rootDir = (options == null ? void 0 : options.rootDir) || process.cwd();
256
299
  const rootConfig = await loadRootConfig(rootDir);
257
300
  const viteConfig = rootConfig.vite || {};
258
- const renderModulePath = path4.resolve(__dirname2, "./render.js");
301
+ const renderModulePath = path4.resolve(__dirname, "./render.js");
259
302
  const pages = [];
260
303
  if (await isDirectory(path4.join(rootDir, "routes"))) {
261
304
  const pageFiles = await glob2(path4.join(rootDir, "routes/**/*"));
@@ -288,6 +331,7 @@ async function getMiddlewares(options) {
288
331
  }
289
332
  const viteServer = await createViteServer({
290
333
  ...viteConfig,
334
+ mode: "development",
291
335
  server: { middlewareMode: true },
292
336
  appType: "custom",
293
337
  optimizeDeps: {
@@ -304,20 +348,28 @@ async function getMiddlewares(options) {
304
348
  });
305
349
  const rootMiddleware = async (req, res, next) => {
306
350
  const url = req.originalUrl;
307
- const render = await viteServer.ssrLoadModule(renderModulePath);
308
- const renderer = new render.Renderer(rootConfig);
351
+ let renderer = null;
309
352
  try {
353
+ const render = await viteServer.ssrLoadModule(renderModulePath);
354
+ renderer = new render.Renderer(rootConfig);
310
355
  const assetMap = new DevServerAssetMap(viteServer.moduleGraph);
311
356
  const data = await renderer.render(url, {
312
357
  assetMap
313
358
  });
314
- const html = await htmlMinify(await viteServer.transformIndexHtml(url, data.html || ""));
359
+ let html = await viteServer.transformIndexHtml(url, data.html || "");
360
+ if (rootConfig.minifyHtml !== false) {
361
+ html = await htmlMinify(html);
362
+ }
315
363
  res.status(200).set({ "Content-Type": "text/html" }).end(html);
316
364
  } catch (e) {
317
365
  viteServer.ssrFixStacktrace(e);
318
366
  try {
319
- const { html } = await renderer.renderError(e);
320
- res.status(500).set({ "Content-Type": "text/html" }).end(html);
367
+ if (renderer) {
368
+ const { html } = await renderer.renderError(e);
369
+ res.status(500).set({ "Content-Type": "text/html" }).end(html);
370
+ } else {
371
+ next(e);
372
+ }
321
373
  } catch (e2) {
322
374
  console.error("failed to render custom error");
323
375
  console.error(e2);
@@ -327,8 +379,13 @@ async function getMiddlewares(options) {
327
379
  };
328
380
  return [viteServer.middlewares, rootMiddleware];
329
381
  }
330
- function dev(rootDir) {
331
- createServer({ rootDir });
382
+ async function dev(rootDir) {
383
+ process.env.NODE_ENV = "development";
384
+ try {
385
+ await createServer({ rootDir });
386
+ } catch (err) {
387
+ console.error("an error occurred");
388
+ }
332
389
  }
333
390
 
334
391
  // src/cli/commands/build.ts
@@ -341,17 +398,24 @@ import { build as viteBuild } from "vite";
341
398
  // src/render/asset-map/build-asset-map.ts
342
399
  import path5 from "path";
343
400
  var BuildAssetMap = class {
344
- constructor(manifest) {
401
+ constructor(manifest, options) {
345
402
  this.manifest = manifest;
403
+ this.elementMap = options.elementMap;
346
404
  this.moduleIdToAsset = /* @__PURE__ */ new Map();
347
405
  Object.keys(this.manifest).forEach((manifestKey) => {
348
406
  const moduleId = `/${manifestKey}`;
349
- this.moduleIdToAsset.set(moduleId, new BuildAsset(this, moduleId, this.manifest[manifestKey]));
407
+ this.moduleIdToAsset.set(
408
+ moduleId,
409
+ new BuildAsset(this, moduleId, this.manifest[manifestKey])
410
+ );
350
411
  });
351
412
  }
352
413
  async get(moduleId) {
353
414
  return this.moduleIdToAsset.get(moduleId) || null;
354
415
  }
416
+ isElement(moduleId) {
417
+ return Object.values(this.elementMap).includes(moduleId);
418
+ }
355
419
  toJson() {
356
420
  const result = {};
357
421
  for (const moduleId of this.moduleIdToAsset.keys()) {
@@ -366,8 +430,12 @@ var BuildAsset = class {
366
430
  this.moduleId = moduleId;
367
431
  this.manifestData = manifestData;
368
432
  this.assetUrl = `/${manifestData.file}`;
369
- this.importedModules = (this.manifestData.imports || []).map((relPath) => `/${relPath}`);
370
- this.importedCss = (this.manifestData.css || []).map((relPath) => `/${relPath}`);
433
+ this.importedModules = (this.manifestData.imports || []).map(
434
+ (relPath) => `/${relPath}`
435
+ );
436
+ this.importedCss = (this.manifestData.css || []).map(
437
+ (relPath) => `/${relPath}`
438
+ );
371
439
  }
372
440
  async getCssDeps() {
373
441
  const visited = /* @__PURE__ */ new Set();
@@ -393,13 +461,15 @@ var BuildAsset = class {
393
461
  }
394
462
  visited.add(asset.moduleId);
395
463
  const parts = path5.parse(asset.assetUrl);
396
- if ([".js", ".jsx", ".ts", ".tsx"].includes(parts.ext) && asset.moduleId.includes("/elements/")) {
464
+ if ([".js", ".jsx", ".ts", ".tsx"].includes(parts.ext) && this.assetMap.isElement(asset.moduleId)) {
397
465
  urls.add(asset.assetUrl);
398
466
  }
399
- await Promise.all(asset.importedModules.map(async (moduleId) => {
400
- const importedAsset = await this.assetMap.get(moduleId);
401
- this.collectJs(importedAsset, urls, visited);
402
- }));
467
+ await Promise.all(
468
+ asset.importedModules.map(async (moduleId) => {
469
+ const importedAsset = await this.assetMap.get(moduleId);
470
+ this.collectJs(importedAsset, urls, visited);
471
+ })
472
+ );
403
473
  }
404
474
  async collectCss(asset, urls, visited) {
405
475
  if (!asset) {
@@ -415,10 +485,12 @@ var BuildAsset = class {
415
485
  if (asset.importedCss) {
416
486
  asset.importedCss.forEach((cssUrl) => urls.add(cssUrl));
417
487
  }
418
- await Promise.all(asset.importedModules.map(async (moduleId) => {
419
- const importedAsset = await this.assetMap.get(moduleId);
420
- this.collectCss(importedAsset, urls, visited);
421
- }));
488
+ await Promise.all(
489
+ asset.importedModules.map(async (moduleId) => {
490
+ const importedAsset = await this.assetMap.get(moduleId);
491
+ this.collectCss(importedAsset, urls, visited);
492
+ })
493
+ );
422
494
  }
423
495
  toJson() {
424
496
  return {
@@ -431,12 +503,11 @@ var BuildAsset = class {
431
503
  };
432
504
 
433
505
  // src/cli/commands/build.ts
434
- var __dirname3 = path6.dirname(fileURLToPath2(import.meta.url));
435
- async function build(rootDir) {
506
+ var __dirname2 = path6.dirname(fileURLToPath2(import.meta.url));
507
+ async function build(rootProjectDir) {
508
+ var _a;
436
509
  console.log("\u{1F333} Root.js");
437
- if (!rootDir) {
438
- rootDir = process.cwd();
439
- }
510
+ const rootDir = rootProjectDir || process.cwd();
440
511
  const rootConfig = await loadRootConfig(rootDir);
441
512
  const distDir = path6.join(rootDir, "dist");
442
513
  await rmDir(distDir);
@@ -451,15 +522,32 @@ async function build(rootDir) {
451
522
  }
452
523
  });
453
524
  }
525
+ const elementsDirs = [path6.join(rootDir, "elements")];
526
+ const elementsInclude = ((_a = rootConfig.elements) == null ? void 0 : _a.include) || [];
527
+ for (const dirPath of elementsInclude) {
528
+ const elementsDir = path6.resolve(rootDir, dirPath);
529
+ if (!elementsDir.startsWith(rootDir)) {
530
+ throw new Error(
531
+ `the elements dir (${dirPath}) should be relative to the project's root dir (${rootDir})`
532
+ );
533
+ }
534
+ elementsDirs.push(elementsDir);
535
+ }
454
536
  const elements = [];
455
- if (await isDirectory(path6.join(rootDir, "elements"))) {
456
- const elementFiles = await glob3(path6.join(rootDir, "elements/**/*"));
457
- elementFiles.forEach((file) => {
458
- const parts = path6.parse(file);
459
- if (isJsFile(parts.base)) {
460
- elements.push(file);
461
- }
462
- });
537
+ const elementMap = {};
538
+ for (const dirPath of elementsDirs) {
539
+ if (await isDirectory(dirPath)) {
540
+ const elementFiles = await glob3("**/*", { cwd: dirPath });
541
+ elementFiles.forEach((file) => {
542
+ const parts = path6.parse(file);
543
+ if (isJsFile(parts.base)) {
544
+ const fullPath = path6.join(dirPath, file);
545
+ const moduleId = fullPath.slice(rootDir.length);
546
+ elements.push(fullPath);
547
+ elementMap[parts.name] = moduleId;
548
+ }
549
+ });
550
+ }
463
551
  }
464
552
  const bundleScripts = [];
465
553
  if (await isDirectory(path6.join(rootDir, "bundles"))) {
@@ -488,7 +576,7 @@ async function build(rootDir) {
488
576
  publicDir: false,
489
577
  build: {
490
578
  rollupOptions: {
491
- input: [path6.resolve(__dirname3, "./render.js")],
579
+ input: [path6.resolve(__dirname2, "./render.js")],
492
580
  output: {
493
581
  format: "esm",
494
582
  chunkFileNames: "chunks/[name].[hash].js",
@@ -532,9 +620,14 @@ async function build(rootDir) {
532
620
  reportCompressedSize: false
533
621
  }
534
622
  });
535
- const manifest = await loadJson(path6.join(distDir, "client/manifest.json"));
536
- const assetMap = new BuildAssetMap(manifest);
537
- writeFile(path6.join(distDir, "client/root-manifest.json"), JSON.stringify(assetMap.toJson(), null, 2));
623
+ const manifest = await loadJson(
624
+ path6.join(distDir, "client/manifest.json")
625
+ );
626
+ const assetMap = new BuildAssetMap(manifest, { elementMap });
627
+ writeFile(
628
+ path6.join(distDir, "client/root-manifest.json"),
629
+ JSON.stringify(assetMap.toJson(), null, 2)
630
+ );
538
631
  const buildDir = path6.join(distDir, "html");
539
632
  const publicDir = path6.join(rootDir, "public");
540
633
  if (fsExtra2.existsSync(path6.join(rootDir, "public"))) {
@@ -547,21 +640,23 @@ async function build(rootDir) {
547
640
  const render = await import(path6.join(distDir, "server/render.js"));
548
641
  const renderer = new render.Renderer(rootConfig);
549
642
  const sitemap = await renderer.getSitemap();
550
- await Promise.all(Object.keys(sitemap).map(async (urlPath) => {
551
- const { route, params } = sitemap[urlPath];
552
- const data = await renderer.renderRoute(route, {
553
- assetMap,
554
- routeParams: params
555
- });
556
- let outPath = path6.join(distDir, `html${urlPath}`, "index.html");
557
- if (outPath.endsWith("404/index.html")) {
558
- outPath = outPath.replace("404/index.html", "404.html");
559
- }
560
- const html = await htmlMinify(data.html || "");
561
- await writeFile(outPath, html);
562
- const relPath = outPath.slice(path6.dirname(distDir).length + 1);
563
- console.log(`saved ${relPath}`);
564
- }));
643
+ await Promise.all(
644
+ Object.keys(sitemap).map(async (urlPath) => {
645
+ const { route, params } = sitemap[urlPath];
646
+ const data = await renderer.renderRoute(route, {
647
+ assetMap,
648
+ routeParams: params
649
+ });
650
+ let outPath = path6.join(distDir, `html${urlPath}`, "index.html");
651
+ if (outPath.endsWith("404/index.html")) {
652
+ outPath = outPath.replace("404/index.html", "404.html");
653
+ }
654
+ const html = await htmlMinify(data.html || "");
655
+ await writeFile(outPath, html);
656
+ const relPath = outPath.slice(path6.dirname(distDir).length + 1);
657
+ console.log(`saved ${relPath}`);
658
+ })
659
+ );
565
660
  }
566
661
 
567
662
  // src/cli/commands/start.ts