Consolidate Lit repositories: figma-to-lit, figma_lit_example, lit-3d-piano, lit-calculator, lit-code-editor, lit-draggable-dom, lit-file-based-routing, lit-force-graph, lit-html-editor, lit-html-table, lit-modules, lit-native, lit-node-editor, lit-sheet-music, lit-starter-ts, lit-vscode-extension, lit-wmr, vite-lit-capacitor, vite-lit-element-starter, vite-rxdb-lit

This commit is contained in:
2026-05-16 22:17:12 -07:00
parent 4dc3c8a3ff
commit 95667e3038
527 changed files with 63872 additions and 54 deletions
@@ -0,0 +1,5 @@
node_modules
.DS_Store
dist
types
*.local
+269
View File
@@ -0,0 +1,269 @@
{
"name": "lit-file-router",
"version": "0.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@types/node": {
"version": "17.0.8",
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.8.tgz",
"integrity": "sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg==",
"dev": true
},
"esbuild": {
"version": "0.13.15",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.15.tgz",
"integrity": "sha512-raCxt02HBKv8RJxE8vkTSCXGIyKHdEdGfUmiYb8wnabnaEmHzyW7DCHb5tEN0xU8ryqg5xw54mcwnYkC4x3AIw==",
"dev": true,
"requires": {
"esbuild-android-arm64": "0.13.15",
"esbuild-darwin-64": "0.13.15",
"esbuild-darwin-arm64": "0.13.15",
"esbuild-freebsd-64": "0.13.15",
"esbuild-freebsd-arm64": "0.13.15",
"esbuild-linux-32": "0.13.15",
"esbuild-linux-64": "0.13.15",
"esbuild-linux-arm": "0.13.15",
"esbuild-linux-arm64": "0.13.15",
"esbuild-linux-mips64le": "0.13.15",
"esbuild-linux-ppc64le": "0.13.15",
"esbuild-netbsd-64": "0.13.15",
"esbuild-openbsd-64": "0.13.15",
"esbuild-sunos-64": "0.13.15",
"esbuild-windows-32": "0.13.15",
"esbuild-windows-64": "0.13.15",
"esbuild-windows-arm64": "0.13.15"
}
},
"esbuild-android-arm64": {
"version": "0.13.15",
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.15.tgz",
"integrity": "sha512-m602nft/XXeO8YQPUDVoHfjyRVPdPgjyyXOxZ44MK/agewFFkPa8tUo6lAzSWh5Ui5PB4KR9UIFTSBKh/RrCmg==",
"dev": true,
"optional": true
},
"esbuild-darwin-64": {
"version": "0.13.15",
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.15.tgz",
"integrity": "sha512-ihOQRGs2yyp7t5bArCwnvn2Atr6X4axqPpEdCFPVp7iUj4cVSdisgvEKdNR7yH3JDjW6aQDw40iQFoTqejqxvQ==",
"dev": true,
"optional": true
},
"esbuild-darwin-arm64": {
"version": "0.13.15",
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.15.tgz",
"integrity": "sha512-i1FZssTVxUqNlJ6cBTj5YQj4imWy3m49RZRnHhLpefFIh0To05ow9DTrXROTE1urGTQCloFUXTX8QfGJy1P8dQ==",
"dev": true,
"optional": true
},
"esbuild-freebsd-64": {
"version": "0.13.15",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.15.tgz",
"integrity": "sha512-G3dLBXUI6lC6Z09/x+WtXBXbOYQZ0E8TDBqvn7aMaOCzryJs8LyVXKY4CPnHFXZAbSwkCbqiPuSQ1+HhrNk7EA==",
"dev": true,
"optional": true
},
"esbuild-freebsd-arm64": {
"version": "0.13.15",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.15.tgz",
"integrity": "sha512-KJx0fzEDf1uhNOZQStV4ujg30WlnwqUASaGSFPhznLM/bbheu9HhqZ6mJJZM32lkyfGJikw0jg7v3S0oAvtvQQ==",
"dev": true,
"optional": true
},
"esbuild-linux-32": {
"version": "0.13.15",
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.15.tgz",
"integrity": "sha512-ZvTBPk0YWCLMCXiFmD5EUtB30zIPvC5Itxz0mdTu/xZBbbHJftQgLWY49wEPSn2T/TxahYCRDWun5smRa0Tu+g==",
"dev": true,
"optional": true
},
"esbuild-linux-64": {
"version": "0.13.15",
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.15.tgz",
"integrity": "sha512-eCKzkNSLywNeQTRBxJRQ0jxRCl2YWdMB3+PkWFo2BBQYC5mISLIVIjThNtn6HUNqua1pnvgP5xX0nHbZbPj5oA==",
"dev": true,
"optional": true
},
"esbuild-linux-arm": {
"version": "0.13.15",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.15.tgz",
"integrity": "sha512-wUHttDi/ol0tD8ZgUMDH8Ef7IbDX+/UsWJOXaAyTdkT7Yy9ZBqPg8bgB/Dn3CZ9SBpNieozrPRHm0BGww7W/jA==",
"dev": true,
"optional": true
},
"esbuild-linux-arm64": {
"version": "0.13.15",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.15.tgz",
"integrity": "sha512-bYpuUlN6qYU9slzr/ltyLTR9YTBS7qUDymO8SV7kjeNext61OdmqFAzuVZom+OLW1HPHseBfJ/JfdSlx8oTUoA==",
"dev": true,
"optional": true
},
"esbuild-linux-mips64le": {
"version": "0.13.15",
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.15.tgz",
"integrity": "sha512-KlVjIG828uFPyJkO/8gKwy9RbXhCEUeFsCGOJBepUlpa7G8/SeZgncUEz/tOOUJTcWMTmFMtdd3GElGyAtbSWg==",
"dev": true,
"optional": true
},
"esbuild-linux-ppc64le": {
"version": "0.13.15",
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.15.tgz",
"integrity": "sha512-h6gYF+OsaqEuBjeesTBtUPw0bmiDu7eAeuc2OEH9S6mV9/jPhPdhOWzdeshb0BskRZxPhxPOjqZ+/OqLcxQwEQ==",
"dev": true,
"optional": true
},
"esbuild-netbsd-64": {
"version": "0.13.15",
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.15.tgz",
"integrity": "sha512-3+yE9emwoevLMyvu+iR3rsa+Xwhie7ZEHMGDQ6dkqP/ndFzRHkobHUKTe+NCApSqG5ce2z4rFu+NX/UHnxlh3w==",
"dev": true,
"optional": true
},
"esbuild-openbsd-64": {
"version": "0.13.15",
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.15.tgz",
"integrity": "sha512-wTfvtwYJYAFL1fSs8yHIdf5GEE4NkbtbXtjLWjM3Cw8mmQKqsg8kTiqJ9NJQe5NX/5Qlo7Xd9r1yKMMkHllp5g==",
"dev": true,
"optional": true
},
"esbuild-sunos-64": {
"version": "0.13.15",
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.15.tgz",
"integrity": "sha512-lbivT9Bx3t1iWWrSnGyBP9ODriEvWDRiweAs69vI+miJoeKwHWOComSRukttbuzjZ8r1q0mQJ8Z7yUsDJ3hKdw==",
"dev": true,
"optional": true
},
"esbuild-windows-32": {
"version": "0.13.15",
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.15.tgz",
"integrity": "sha512-fDMEf2g3SsJ599MBr50cY5ve5lP1wyVwTe6aLJsM01KtxyKkB4UT+fc5MXQFn3RLrAIAZOG+tHC+yXObpSn7Nw==",
"dev": true,
"optional": true
},
"esbuild-windows-64": {
"version": "0.13.15",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.15.tgz",
"integrity": "sha512-9aMsPRGDWCd3bGjUIKG/ZOJPKsiztlxl/Q3C1XDswO6eNX/Jtwu4M+jb6YDH9hRSUflQWX0XKAfWzgy5Wk54JQ==",
"dev": true,
"optional": true
},
"esbuild-windows-arm64": {
"version": "0.13.15",
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.15.tgz",
"integrity": "sha512-zzvyCVVpbwQQATaf3IG8mu1IwGEiDxKkYUdA4FpoCHi1KtPa13jeScYDjlW0Qh+ebWzpKfR2ZwvqAQkSWNcKjA==",
"dev": true,
"optional": true
},
"fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"optional": true
},
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"requires": {
"function-bind": "^1.1.1"
}
},
"is-core-module": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz",
"integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==",
"dev": true,
"requires": {
"has": "^1.0.3"
}
},
"nanoid": {
"version": "3.1.30",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz",
"integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==",
"dev": true
},
"path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
"picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
"dev": true
},
"postcss": {
"version": "8.4.5",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz",
"integrity": "sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==",
"dev": true,
"requires": {
"nanoid": "^3.1.30",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.1"
}
},
"resolve": {
"version": "1.21.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz",
"integrity": "sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==",
"dev": true,
"requires": {
"is-core-module": "^2.8.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
}
},
"rollup": {
"version": "2.63.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.63.0.tgz",
"integrity": "sha512-nps0idjmD+NXl6OREfyYXMn/dar3WGcyKn+KBzPdaLecub3x/LrId0wUcthcr8oZUAcZAR8NKcfGGFlNgGL1kQ==",
"dev": true,
"requires": {
"fsevents": "~2.3.2"
}
},
"source-map-js": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.1.tgz",
"integrity": "sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==",
"dev": true
},
"supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true
},
"typescript": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz",
"integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==",
"dev": true
},
"vite": {
"version": "2.7.10",
"resolved": "https://registry.npmjs.org/vite/-/vite-2.7.10.tgz",
"integrity": "sha512-KEY96ntXUid1/xJihJbgmLZx7QSC2D4Tui0FdS0Old5OokYzFclcofhtxtjDdGOk/fFpPbHv9yw88+rB93Tb8w==",
"dev": true,
"requires": {
"esbuild": "^0.13.12",
"fsevents": "~2.3.2",
"postcss": "^8.4.5",
"resolve": "^1.20.0",
"rollup": "^2.59.0"
}
}
}
}
@@ -0,0 +1,20 @@
{
"type": "module",
"name": "lit-file-router",
"version": "0.0.0",
"main": "dist/index.js",
"types": "types/index.d.ts",
"files": [
"dist",
"types"
],
"scripts": {
"preinstall": "npm run build",
"build": "tsc"
},
"dependencies": {},
"devDependencies": {
"@types/node": "^17.0.8",
"typescript": "^4.4.4"
}
}
@@ -0,0 +1,56 @@
export function analyzePage(path: string, content: string): WebComponent[] {
const components: WebComponent[] = [];
// Check for export function loader( or export async function loader(
let hasLoader = false;
const lines = content.split("\n");
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (
line.includes("export function loader(") ||
line.includes("export async function loader(")
) {
hasLoader = true;
break;
}
}
// Look for @customElement decorator and get the value
const regex = /@customElement\("([^"]+)"\)/g;
const matches = content.match(regex);
if (matches) {
for (const match of matches) {
const result = match.match(/@customElement\("([^"]+)"\)/);
if (result) {
const name = result[1];
components.push({
name,
path,
hasLoader,
});
}
}
}
// Look for customElements.define() and get the value
const regex2 = /customElements\.define\("([^"]+)"/g;
const matches2 = content.match(regex2);
if (matches2) {
for (const match of matches2) {
const result = match.match(/customElements\.define\("([^"]+)"/);
if (result) {
const name = result[1];
components.push({
name,
path,
hasLoader,
});
}
}
}
return components;
}
export interface WebComponent {
name: string;
path: string;
alias?: string;
hasLoader: boolean;
}
@@ -0,0 +1,264 @@
import { WebComponent } from "./analyze-page";
import { StringBuilder } from "./string-builder";
export function generateApp(
sb: StringBuilder,
components: WebComponent[],
options: { staticImports: boolean; cacheAll: boolean; showLoading: boolean }
) {
sb.writeln(`import { html, css, LitElement } from "lit";`);
sb.writeln(
`import { customElement, property, state } from "lit/decorators.js";`
);
sb.writeln();
sb.writeAll([
'@customElement("generated-app")',
"export class GeneratedApp extends LitElement {",
" static styles = css`",
" main {",
" width: 100%;",
" height: 100%;",
" }",
...(options.showLoading
? [
" progress {",
" position: absolute;",
" bottom: 0;",
" width: calc(100% - 1rem);",
" z-index: 1;",
" left: 0.5rem;",
" right: 0.5rem;",
" }",
]
: []),
" `;",
"",
" @property() hash = 'true';",
" @property() base = '/';",
...(options.showLoading ? [" @state() loading = false;"] : []),
" @property() route = this.getCurrentRoute();",
" @state() child = document.createElement('main');",
"",
]);
if (options.cacheAll) {
sb.writeln(" dataCache = new Map<string, any>();");
}
sb.writeln(" components: Map<string, Route> = new Map([");
for (const component of components) {
let path = component.path.replace("./src/pages/", "/");
path = path.replace(".ts", "");
path = path.replace(".js", "");
path = path.replace("/index", "/");
// Check if path contains . and convert to /
if (path.indexOf(".") > -1) {
path = path.split(".").join("/");
}
if (path == "/root") path = "";
// sb.writeln(` ["${path}", "${component.name}"],`);]
sb.writeln(` ["${path}", {
component: "${component.name}",`);
const componentImportPath = component.path
.replace("./src/", "./")
.replace(".ts", ".js");
if (component.hasLoader) {
if (options.staticImports) {
sb.writeln(` loadData: async () => {
return ${component.alias}.loader;
},`);
} else {
sb.writeln(` loadData: async () => {
const {loader} = await import("${componentImportPath}");
return loader;
},`);
}
} else {
sb.writeln(` loadData: async () => null,`);
}
if (options.staticImports) {
sb.writeln(` loadImport: async () => ${component.alias},
}],`);
} else {
sb.writeln(` loadImport: () => import("${componentImportPath}"),
}],`);
}
}
sb.writeAll([
" ]);",
"",
" firstUpdated() {",
' window.addEventListener("hashchange", () => {',
" const oldRoute = this.route;",
" this.route = this.getCurrentRoute();",
" this.updateTree(oldRoute);",
" });",
" this.updateTree();",
" }",
"",
" render() {",
" return html` ${this.child} `;",
" }",
"",
" refresh() {",
" return this.updateTree();",
" }",
"",
" private async updateTree(oldRoute?:string) {",
" this.checkForIndex();",
" if (oldRoute) {",
" // TODO: Get delta between old and new route",
" }",
...(options.showLoading
? [
' const loadingElem = document.createElement("progress");',
" this.child.appendChild(loadingElem);",
]
: []),
" const tree = await this.renderTree();",
" // Remove children",
" while (this.child.firstChild) {",
" this.child.removeChild(this.child.firstChild);",
" }",
" // Add new children",
" if (tree) this.child.appendChild(tree);",
" this.requestUpdate();",
" }",
"",
" private async renderTree() {",
" let _route = this.route;",
" const args = this.getArgsForRoute(_route);",
" const elements: Element[] = [];",
' if (_route !== "/") {',
" while (_route.length > 0) {",
" const newChild = await this.getComponent(_route, args);",
" if (!newChild && _route === this.route) {",
' const noChild = await this.getComponent("/404", args);',
" if (noChild) elements.push(noChild);",
" break;",
" }",
" if (newChild) elements.push(newChild);",
' const parts = _route.split("/");',
" parts.pop();",
' _route = parts.join("/");',
' if (_route === "/") break;',
" }",
' } else if (_route === "/") {',
' const indexChild = await this.getComponent("/", args);',
" if (indexChild) elements.push(indexChild);",
" } else {",
' const noChild = await this.getComponent("/404", args);',
" if (noChild) elements.push(noChild);",
" }",
' const rootChild = await this.getComponent("", args);',
" if (rootChild) elements.push(rootChild);",
" let idx = elements.length - 1;",
" while (idx >= 1) {",
" const parent = elements[idx];",
" const child = elements[idx - 1];",
" if (child && parent) {",
" parent.appendChild(child);",
" }",
" idx--;",
" }",
" return elements.pop();",
" }",
"",
" private async getComponent(",
" path: string,",
" args: RegExpMatchArray | null",
" ) {",
" const applyArgs = (value: string, apply: boolean, data?: any) => {",
" const elem = document.createElement(value);",
" if (apply && args?.groups) {",
" for (const [key, value] of Object.entries(args.groups)) {",
" elem.setAttribute(key, value);",
" }",
" }",
" if (data) (elem as any).data = data;",
" return elem;",
" };",
" for (const [key, value] of Array.from(this.components.entries())) {",
" const hasArgs = path.match(fixRegex(key)) !== null;",
" if (key === path || path.match(fixRegex(key)) !== null) {",
...(options.cacheAll
? [
" const cacheKey = `${path}:${value.component}`;",
" if (this.dataCache.has(cacheKey)) {",
" const data = this.dataCache.get(cacheKey)!;",
...(options.staticImports
? []
: [" await value.loadImport();"]),
" return applyArgs(value.component, hasArgs, data);",
" }",
]
: []),
" const getLoader = await value.loadData();",
" if (getLoader) {",
" const componentData = await getLoader(this.route, args ? Object(args)['groups'] : {});",
...(options.cacheAll
? [" this.dataCache.set(cacheKey, componentData);"]
: []),
...(options.staticImports ? [] : [" await value.loadImport();"]),
" return applyArgs(value.component, hasArgs, componentData);",
" }",
...(options.staticImports ? [] : [" await value.loadImport();"]),
" return applyArgs(value.component, hasArgs);",
" }",
" }",
" return null;",
" }",
"",
" private getArgsForRoute(route: string): RegExpMatchArray | null {",
" for (const key of Array.from(this.components.keys())) {",
" const regMatch = route.match(fixRegex(key));",
" if (regMatch !== null) return regMatch;",
" }",
" return null;",
" }",
"",
" private checkForIndex() {",
' if (this.route === "" || this.route === "/") location.hash = "#/";',
' if (this.route.endsWith("/")) return;',
" const indexArgs = this.getArgsForRoute(`${this.route}/`);",
" if (indexArgs !== null) {",
" location.hash = `#${this.route}/`;",
" }",
" }",
"",
" private getCurrentRoute() {",
' let route = "/";',
' if (this.hash === "true" && window.location.hash.length > 0) {',
" route = window.location.hash.slice(1);",
' } else if (this.hash === "false") {',
' const baseUrl = this.getAttribute("base") ?? "";',
" route = window.location.pathname.slice(baseUrl.length);",
" }",
" console.debug(`current route: ${route}`);",
" return route;",
" }",
"}",
"",
"function fixRegex(route: string): RegExp {",
' const variableRegex = "[a-zA-Z0-9_-]+";',
" const nameWithParameters = route.replace(",
" new RegExp(`:(${variableRegex})`),",
" (match) => {",
" const groupName = match.slice(1);",
" return `(?<${groupName}>[a-zA-Z0-9_\\\\-.,:;+*^%$@!]+)`;",
" }",
" );",
" return new RegExp(`^${nameWithParameters}$`);",
"}",
"",
"interface Route {",
" component: string;",
" loadImport: () => Promise<any>;",
" loadData: () => Promise<RouteLoader | null>;",
"}",
"",
"type RouteLoader = (",
" route: string,",
" args: { [key: string]: any }",
") => Promise<any>;",
]);
sb.writeln();
}
@@ -0,0 +1,91 @@
import * as fs from "fs";
import { analyzePage, WebComponent } from "./analyze-page.js";
import { generateApp } from "./generate-app.js";
import { StringBuilder } from "./string-builder.js";
let inputDir = "./src/pages";
let outputFile = "./src/generated-app.ts";
let staticImports = true;
let cacheAll = false;
let showLoading = false;
const args = process.argv.slice(2);
if (args.length == 0) {
analyzePages();
} else {
// Check for -dynamic-imports=false
const noDynamicImports = args.indexOf("--dynamic-imports=false") !== -1;
staticImports = noDynamicImports;
// Check for --input-dir=<dir>
const inputDirArg = args.find((arg) => arg.startsWith("--input-dir="));
if (inputDirArg) {
inputDir = inputDirArg.replace("--input-dir=", "");
}
// Check for --output-file=<file>
const outputFileArg = args.find((arg) => arg.startsWith("--output-file="));
if (outputFileArg) {
outputFile = outputFileArg.replace("--output-file=", "");
}
// Check for --cache-all
const cacheAllArg = args.find((arg) => arg.startsWith("--cache-all"));
if (cacheAllArg) {
cacheAll = true;
}
// Check for --show-loading=false
const showLoadingArg = args.find((arg) =>
arg.startsWith("--show-loading=true")
);
showLoading = showLoadingArg ? true : false;
analyzePages();
}
function readPagesDirectory(
dir: string,
pages: { path: string; content: string }[]
) {
const files = fs.readdirSync(dir);
for (const file of files) {
if (fs.statSync(`${dir}/${file}`).isDirectory()) {
readPagesDirectory(`${dir}/${file}`, pages);
} else {
const path = `${dir}/${file}`;
const content = fs.readFileSync(path, "utf8");
pages.push({ path, content });
}
}
}
async function analyzePages() {
const sb = new StringBuilder();
// sb.writeln("// @ts-nocheck");
// sb.writeln();
let i = 0;
if (staticImports) {
sb.writeln(`// Page Routes`);
}
const components: WebComponent[] = [];
const pages: { path: string; content: string }[] = [];
readPagesDirectory(inputDir, pages);
for (const { path, content } of pages) {
// Add import
const results = analyzePage(path, content);
if (results.length > 0) {
const c = results[0];
if (staticImports) {
const filePath = path.replace("./src/", "./");
const jsPath = filePath.replace(".ts", ".js");
c.alias = `route${i}`;
sb.writeln(`import * as ${c.alias} from "${jsPath}";`);
// if (c.hasLoader) {
// sb.writeln(`import {loader as ${c.alias}Loader} from "${jsPath}";`);
// }
}
components.push(c);
}
i++;
}
sb.writeln();
generateApp(sb, components, { staticImports, cacheAll, showLoading });
const output = sb.toString();
fs.writeFileSync(outputFile, output);
}
@@ -0,0 +1,20 @@
export class StringBuilder {
private _lines: string[] = [];
write(line: string = ""): void {
this._lines.push(line);
}
writeln(line: string = ""): void {
this._lines.push(line);
this._lines.push("\n");
}
writeAll(lines: string[]): void {
this._lines.push(lines.join("\n"));
}
toString(): string {
return this._lines.join("");
}
}
@@ -0,0 +1,22 @@
{
"compilerOptions": {
"module": "esnext",
"lib": ["es2017", "dom", "dom.iterable"],
"declaration": true,
"emitDeclarationOnly": false,
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"forceConsistentCasingInFileNames": true,
"useDefineForClassFields": false
},
"include": ["src/**/*.ts"],
"exclude": []
}