@temir.ra/create-hono-spa 0.2.0 → 0.3.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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Version 0
2
2
 
3
+ ## 0.3.0
4
+
5
+ 1. Updated from `@temir.ra/create-template@0.1.3`.
6
+
7
+ ## 0.2.1
8
+
9
+ 1. Updated files from `ts-lib@0.6.3` template.
10
+
3
11
  ## 0.2.0
4
12
 
5
13
  1. Updated documentation and configs to TypeScript 6.
package/README.md CHANGED
@@ -5,63 +5,48 @@ A template for single-page applications distributed as Hono sub-app libraries. T
5
5
  ## Table of Contents
6
6
 
7
7
  1. [Quick Start](#quick-start)
8
- 2. [AI Assistant Context](#ai-assistant-context)
9
- 3. [Documentation](#documentation)
8
+ 2. [Documentation](#documentation)
10
9
  1. [`template/`](#template)
11
10
  2. [Architecture](#architecture)
12
11
  3. [`src/spa.ts`](#srcspa-ts)
13
12
  4. [`src/constants.ts`](#srcconstants-ts)
14
13
  5. [`assets/`](#assets)
15
- 4. [DevOps](#devops)
14
+ 3. [DevOps](#devops)
16
15
  1. [Change Management](#change-management)
17
16
  2. [Publish](#publish)
18
- 1. [npmjs.org](#npmjsorg)
19
- 2. [Custom registry](#custom-registry)
20
17
 
21
18
  # Quick Start
22
19
 
23
- *`bun create` caches the template package - a newer published version will not be picked up automatically. Pin the version or clear the cache to use the latest.*
24
-
25
20
  ```bash
26
21
  # placeholder:
22
+ # <TEMPLATE_PACKAGE: @temir.ra/create-hono-spa
23
+ # <TEMPLATE_NAME: @temir.ra/hono-spa
27
24
  # <NEW_PACKAGE: <NEW_PACKAGE>
25
+ # is used as:
26
+ # - the path where the package is created
27
+ # - the "name" field in the generated package.json
28
28
  # <@_VERSION: <@_VERSION>
29
29
 
30
- # identify the latest version of the template package as <@_VERSION.
30
+ # pinned version
31
31
  bun info "@temir.ra/create-hono-spa" version
32
- # create a new library from the template version
33
32
  bun create --no-install --no-git "@temir.ra/hono-spa<@_VERSION>" <NEW_PACKAGE>
34
33
 
35
- # or
36
-
37
- # clear package manager cache to ensure the latest template version is used
34
+ # latest
35
+ # clear the cache to pick up the latest version
38
36
  bun pm cache rm
39
- # create a new library from the latest template version
40
37
  bun create --no-install --no-git "@temir.ra/hono-spa" <NEW_PACKAGE>
41
38
 
42
- # dependencies must be installed manually
39
+ # templates only copy files, run install and any setup scripts manually
43
40
  cd <NEW_PACKAGE>
44
41
  bun install
45
42
  ```
46
43
 
47
- # AI Assistant Context
48
-
49
- To generate an AI coding assistant context file for this project:
50
-
51
- > Generate an AI coding assistant context file. Analyze the project structure and source files, using this README as the primary reference for architecture and conventions. Give particular attention to: the SPA-as-library abstraction and how assets are served via import.meta.url without any file copying on the consuming server, the `./spa` export condition and createSpa() as the library's public API, and the base href patching mechanism that enables client-side routing regardless of mount path.
52
-
53
44
  # Documentation
54
45
 
55
- The following sections explain the configurations and conventions baked into the generated package.
46
+ The following sections explain the configurations and conventions baked into the generated package. Useful when adapting it to fit specific needs.
56
47
 
57
48
  ## `template/`
58
49
 
59
- The files in `template/` were created from the [`@temir.ra/ts-lib@0.5.0`](https://www.npmjs.com/package/@temir.ra/create-ts-lib/v/0.5.0) template and then updated to fit the needs of a Hono SPA template.
60
-
61
- ```powershell
62
- bun create --no-install --no-git --force "@temir.ra/ts-lib@0.5.0" "template/"
63
- ```
64
-
65
50
  [Official documentation](https://hono.dev/docs/) provided by the Hono team is a great resource.
66
51
 
67
52
  ## Architecture
@@ -142,23 +127,16 @@ assets/
142
127
  # DevOps
143
128
 
144
129
  ```bash
145
- # remove dist/ and tsconfig.build.tsbuildinfo
146
- bun run clean
147
-
148
- # remove dist/ only
149
- bun run clean:dist
150
-
151
- # remove tsconfig.build.tsbuildinfo only
152
- bun run clean:tsbuildinfo
130
+ bun install
153
131
 
154
- # compile + bundle
132
+ bun run clean
155
133
  bun run build
134
+ bun run tests
156
135
 
157
- # create a new test hono spa in example/
158
- bun run dist/cli.bundle.js -- example
159
- ```
136
+ bun run dev
160
137
 
161
- **Publish** - see [Publish](#publish).
138
+ # see publish section for publish instructions
139
+ ```
162
140
 
163
141
  ## Change Management
164
142
 
@@ -167,20 +145,11 @@ bun run dist/cli.bundle.js -- example
167
145
  3. Bump the version in [`package.json`](package.json).
168
146
  4. Add an entry for the new version in [`CHANGELOG.md`](CHANGELOG.md).
169
147
  5. Pull request the branch.
170
- 6. After merge, run `bun run build` - ensures artifacts are current before publish.
148
+ 6. Ensure package artifacts are current.
171
149
  7. Publish.
172
150
 
173
151
  ## Publish
174
152
 
175
- See the following sources to configure the target registry and authentication.
176
-
177
- - [Configuring npm - `npmrc`](https://docs.npmjs.com/cli/v10/configuring-npm/npmrc)
178
- - [Bun package manager - `install.registry`](https://bun.com/docs/runtime/bunfig#install-scopes)
179
-
180
- ⚠️ Package Scope and the authentication for the target registry must be aligned.
181
-
182
- ### `npmjs.org`
183
-
184
153
  Publish to the public npm registry.
185
154
 
186
155
  ```powershell
@@ -189,26 +158,3 @@ npm login
189
158
  # publish
190
159
  bun publish --registry https://registry.npmjs.org/ --access public
191
160
  ```
192
-
193
- ### Custom registry
194
-
195
- ```bash
196
- # placeholder:
197
- # <SCOPE_WITHOUT_AT: <SCOPE_WITHOUT_AT>
198
- # <REGISTRY_URL: <REGISTRY_URL>
199
- # <BUN_PUBLISH_AUTH_TOKEN: <BUN_PUBLISH_AUTH_TOKEN>
200
- ```
201
-
202
- `~/.bunfig.toml` or `bunfig.toml`:
203
-
204
- ```toml
205
- [install.scopes]
206
- "<SCOPE_WITHOUT_AT>" = { url = "<REGISTRY_URL>", token = "$BUN_PUBLISH_AUTH_TOKEN" }
207
- ```
208
-
209
- ```powershell
210
- # authenticate
211
- $env:BUN_PUBLISH_AUTH_TOKEN = "<BUN_PUBLISH_AUTH_TOKEN>"
212
- # publish
213
- bun publish
214
- ```
package/buildinfo.txt CHANGED
@@ -1 +1 @@
1
- 0.2.0+40f404b
1
+ 0.3.0+1137cb4
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import{cpSync as s,readFileSync as f,renameSync as u,writeFileSync as P}from"fs";import{resolve as t}from"path";import{resolve as o}from"path";import{fileURLToPath as h}from"url";var d=new URL("../",import.meta.url),n=o(h(d)),m=o(n,"template/"),p=o(n,"CHANGELOG.md"),g=o(n,"buildinfo.txt"),l=o(n,"README.md");try{let r=process.argv[2];if(!r)throw Error("Package name argument is required. Usage: `create-hono-spa <package-name>`");let a=r.replace(/\\/g,"/"),e=t(process.cwd(),a);s(m,e,{recursive:!0}),s(p,t(e,"CHANGELOG-template.md")),s(g,t(e,"buildinfo-template.txt")),s(l,t(e,"README-template.md"));let c=t(e,"package.json"),i=JSON.parse(f(c,"utf-8"));i.name=a,P(c,JSON.stringify(i,null,2)),u(t(e,"gitignore"),t(e,".gitignore")),console.log(`Template has been successfully instantiated at '${e}' with package name '${a}'.`)}catch(r){let a=r instanceof Error?r:Error(String(r));console.error("Error:",a.message),process.exit(1)}
2
+ import{cpSync as s,readFileSync as f,renameSync as u,writeFileSync as P}from"fs";import{resolve as t}from"path";import{resolve as o}from"path";import{fileURLToPath as h}from"url";var d=new URL("../",import.meta.url),n=o(h(d)),m=o(n,"template/"),p=o(n,"CHANGELOG.md"),g=o(n,"buildinfo.txt"),l=o(n,"README.md");try{let r=process.argv[2];if(!r)throw Error('First argument must be the package name (e.g. "my-package" or "@my-scope/my-package").');let a=r.replace(/\\/g,"/"),e=t(process.cwd(),a);s(m,e,{recursive:!0}),s(p,t(e,"CHANGELOG-template.md")),s(g,t(e,"buildinfo-template.txt")),s(l,t(e,"README-template.md"));let c=t(e,"package.json"),i=JSON.parse(f(c,"utf-8"));i.name=a,P(c,JSON.stringify(i,null,2)),u(t(e,"gitignore"),t(e,".gitignore")),console.log(`Template has been successfully instantiated at '${e}' with package name '${a}'.`)}catch(r){let a=r instanceof Error?r:Error(String(r));console.error("Error:",a.message),process.exit(1)}
3
3
 
4
- //# debugId=22A62DDB3C86A9D764756E2164756E21
4
+ //# debugId=C4A0E6EEF1219B4364756E2164756E21
@@ -2,10 +2,10 @@
2
2
  "version": 3,
3
3
  "sources": ["..\\src\\cli.ts", "..\\src\\constants.ts"],
4
4
  "sourcesContent": [
5
- "#!/usr/bin/env node\r\n\r\nimport { cpSync, readFileSync, renameSync, writeFileSync } from 'fs';\r\nimport { resolve, basename } from 'path';\r\nimport {\r\n templatePath,\r\n changelogPath,\r\n buildinfoPath,\r\n readmePath\r\n} from './constants.js';\r\n\r\n\r\ntry {\r\n\r\n const packageNameArgument = process.argv[2];\r\n if (!packageNameArgument) throw new Error('Package name argument is required. Usage: `create-hono-spa <package-name>`');\r\n const packageName = packageNameArgument.replace(/\\\\/g, '/');\r\n\r\n const destinationPath = resolve(process.cwd(), packageName);\r\n\r\n cpSync(templatePath, destinationPath, { recursive: true });\r\n cpSync(changelogPath, resolve(destinationPath, 'CHANGELOG-template.md'));\r\n cpSync(buildinfoPath, resolve(destinationPath, 'buildinfo-template.txt'));\r\n cpSync(readmePath, resolve(destinationPath, 'README-template.md'));\r\n\r\n const packageJsonPath = resolve(destinationPath, 'package.json');\r\n const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));\r\n packageJson.name = packageName;\r\n writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));\r\n\r\n renameSync(resolve(destinationPath, 'gitignore'), resolve(destinationPath, '.gitignore'));\r\n\r\n console.log(`Template has been successfully instantiated at '${destinationPath}' with package name '${packageName}'.`);\r\n\r\n}\r\ncatch (error) {\r\n const err = error instanceof Error ? error : new Error(String(error));\r\n console.error('Error:', err.message);\r\n process.exit(1);\r\n}\r\n",
5
+ "#!/usr/bin/env node\r\n\r\nimport { cpSync, readFileSync, renameSync, writeFileSync } from 'fs';\r\nimport { resolve } from 'path';\r\nimport {\r\n templatePath,\r\n changelogPath,\r\n buildinfoPath,\r\n readmePath\r\n} from './constants.js';\r\n\r\n\r\ntry {\r\n\r\n const packageNameArgument = process.argv[2];\r\n if (!packageNameArgument)\r\n throw new Error('First argument must be the package name (e.g. \"my-package\" or \"@my-scope/my-package\").');\r\n const packageName = packageNameArgument.replace(/\\\\/g, '/');\r\n\r\n const destinationPath = resolve(process.cwd(), packageName);\r\n\r\n cpSync(templatePath, destinationPath, { recursive: true });\r\n cpSync(changelogPath, resolve(destinationPath, 'CHANGELOG-template.md'));\r\n cpSync(buildinfoPath, resolve(destinationPath, 'buildinfo-template.txt'));\r\n cpSync(readmePath, resolve(destinationPath, 'README-template.md'));\r\n\r\n const packageJsonPath = resolve(destinationPath, 'package.json');\r\n const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));\r\n packageJson.name = packageName;\r\n writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));\r\n\r\n renameSync(resolve(destinationPath, 'gitignore'), resolve(destinationPath, '.gitignore'));\r\n\r\n console.log(`Template has been successfully instantiated at '${destinationPath}' with package name '${packageName}'.`);\r\n\r\n}\r\ncatch (error) {\r\n const err = error instanceof Error ? error : new Error(String(error));\r\n console.error('Error:', err.message);\r\n process.exit(1);\r\n}\r\n",
6
6
  "import { resolve } from 'path';\r\nimport { fileURLToPath } from 'url';\r\n\r\n\r\nexport const packageUrl: URL = new URL('../', import.meta.url);\r\nexport const packagePath: string = resolve(fileURLToPath(packageUrl));\r\n\r\nexport const templatePath: string = resolve(packagePath, 'template/');\r\n\r\nexport const changelogPath: string = resolve(packagePath, 'CHANGELOG.md');\r\nexport const buildinfoPath: string = resolve(packagePath, 'buildinfo.txt');\r\nexport const readmePath: string = resolve(packagePath, 'README.md');\r\n"
7
7
  ],
8
- "mappings": ";AAEA,iBAAS,kBAAQ,gBAAc,mBAAY,WAC3C,kBAAS,aCHT,kBAAS,aACT,wBAAS,YAGF,IAAM,EAAkB,IAAI,IAAI,MAAO,YAAY,GAAG,EAChD,EAAsB,EAAQ,EAAc,CAAU,CAAC,EAEvD,EAAuB,EAAQ,EAAa,WAAW,EAEvD,EAAwB,EAAQ,EAAa,cAAc,EAC3D,EAAwB,EAAQ,EAAa,eAAe,EAC5D,EAAqB,EAAQ,EAAa,WAAW,EDClE,GAAI,CAEA,IAAM,EAAsB,QAAQ,KAAK,GACzC,GAAI,CAAC,EAAqB,MAAU,MAAM,4EAA4E,EACtH,IAAM,EAAc,EAAoB,QAAQ,MAAO,GAAG,EAEpD,EAAkB,EAAQ,QAAQ,IAAI,EAAG,CAAW,EAE1D,EAAO,EAAc,EAAiB,CAAE,UAAW,EAAK,CAAC,EACzD,EAAO,EAAe,EAAQ,EAAiB,uBAAuB,CAAC,EACvE,EAAO,EAAe,EAAQ,EAAiB,wBAAwB,CAAC,EACxE,EAAO,EAAY,EAAQ,EAAiB,oBAAoB,CAAC,EAEjE,IAAM,EAAkB,EAAQ,EAAiB,cAAc,EACzD,EAAc,KAAK,MAAM,EAAa,EAAiB,OAAO,CAAC,EACrE,EAAY,KAAO,EACnB,EAAc,EAAiB,KAAK,UAAU,EAAa,KAAM,CAAC,CAAC,EAEnE,EAAW,EAAQ,EAAiB,WAAW,EAAG,EAAQ,EAAiB,YAAY,CAAC,EAExF,QAAQ,IAAI,mDAAmD,yBAAuC,KAAe,EAGzH,MAAO,EAAO,CACV,IAAM,EAAM,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,EACpE,QAAQ,MAAM,SAAU,EAAI,OAAO,EACnC,QAAQ,KAAK,CAAC",
9
- "debugId": "22A62DDB3C86A9D764756E2164756E21",
8
+ "mappings": ";AAEA,iBAAS,kBAAQ,gBAAc,mBAAY,WAC3C,kBAAS,aCHT,kBAAS,aACT,wBAAS,YAGF,IAAM,EAAkB,IAAI,IAAI,MAAO,YAAY,GAAG,EAChD,EAAsB,EAAQ,EAAc,CAAU,CAAC,EAEvD,EAAuB,EAAQ,EAAa,WAAW,EAEvD,EAAwB,EAAQ,EAAa,cAAc,EAC3D,EAAwB,EAAQ,EAAa,eAAe,EAC5D,EAAqB,EAAQ,EAAa,WAAW,EDClE,GAAI,CAEA,IAAM,EAAsB,QAAQ,KAAK,GACzC,GAAI,CAAC,EACD,MAAU,MAAM,wFAAwF,EAC5G,IAAM,EAAc,EAAoB,QAAQ,MAAO,GAAG,EAEpD,EAAkB,EAAQ,QAAQ,IAAI,EAAG,CAAW,EAE1D,EAAO,EAAc,EAAiB,CAAE,UAAW,EAAK,CAAC,EACzD,EAAO,EAAe,EAAQ,EAAiB,uBAAuB,CAAC,EACvE,EAAO,EAAe,EAAQ,EAAiB,wBAAwB,CAAC,EACxE,EAAO,EAAY,EAAQ,EAAiB,oBAAoB,CAAC,EAEjE,IAAM,EAAkB,EAAQ,EAAiB,cAAc,EACzD,EAAc,KAAK,MAAM,EAAa,EAAiB,OAAO,CAAC,EACrE,EAAY,KAAO,EACnB,EAAc,EAAiB,KAAK,UAAU,EAAa,KAAM,CAAC,CAAC,EAEnE,EAAW,EAAQ,EAAiB,WAAW,EAAG,EAAQ,EAAiB,YAAY,CAAC,EAExF,QAAQ,IAAI,mDAAmD,yBAAuC,KAAe,EAGzH,MAAO,EAAO,CACV,IAAM,EAAM,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,EACpE,QAAQ,MAAM,SAAU,EAAI,OAAO,EACnC,QAAQ,KAAK,CAAC",
9
+ "debugId": "C4A0E6EEF1219B4364756E2164756E21",
10
10
  "names": []
11
11
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@temir.ra/create-hono-spa",
3
- "version": "0.2.0",
4
- "description": "SPA template with Hono server backend",
3
+ "version": "0.3.0",
4
+ "description": "SPA template for Hono server backend",
5
5
  "author": "temir.ra",
6
6
  "license": "MIT",
7
7
  "keywords": [
@@ -15,12 +15,16 @@
15
15
  "type": "git",
16
16
  "url": "https://git.chimps.quest/trs/create-hono-spa.git"
17
17
  },
18
+ "private": false,
18
19
  "type": "module",
19
20
  "imports": {
20
21
  "#src/*.js": "./src/*.ts"
21
22
  },
22
23
  "bin": "./dist/cli.bundle.js",
23
24
  "files": [
25
+ "scripts/buildinfo.ts",
26
+ "scripts/build-bundle.ts",
27
+ "scripts/cdn-rewrite-map.json",
24
28
  "dist",
25
29
  "CHANGELOG.md",
26
30
  "buildinfo.txt",
@@ -28,14 +32,15 @@
28
32
  ],
29
33
  "scripts": {
30
34
  "clean:dist": "rm -rf dist/",
31
- "clean:tsbuildinfo": "rm -f tsconfig.build.tsbuildinfo",
35
+ "clean:tsbuildinfo": "rm -f *.tsbuildinfo || true",
32
36
  "clean": "bun run clean:dist && bun run clean:tsbuildinfo",
33
- "prebuild": "bun run scripts/buildinfo.ts",
37
+ "buildinfo": "bun run scripts/buildinfo.ts",
34
38
  "tests": "bun test",
35
- "build": "bun run build:lib && bun run build:cli-bundle",
36
- "build:lib": "tsc --project tsconfig.build.json",
37
- "build:cli-bundle": "bun build src/cli.ts --entry-naming \"[dir]/[name].bundle.[ext]\" --outdir dist --target node --format esm --minify --sourcemap=external",
38
- "typecheck": "tsc --noEmit"
39
+ "typecheck": "tsc --noEmit",
40
+ "reinstall": "rm -rf node_modules && rm -f bun.lock && bun pm cache rm && bun install && bunx tsc --version",
41
+ "prebuild": "bun run buildinfo",
42
+ "build": "bun run build:cli-bundle",
43
+ "build:cli-bundle": "bun build src/cli.ts --entry-naming \"[dir]/[name].bundle.[ext]\" --outdir dist --target node --format esm --minify --sourcemap=external"
39
44
  },
40
45
  "devDependencies": {
41
46
  "@types/bun": "latest",
@@ -0,0 +1,161 @@
1
+ import { readFileSync } from 'fs';
2
+ import { join } from 'path';
3
+
4
+ import CDN_REWRITE_MAP from './cdn-rewrite-map.json';
5
+
6
+
7
+ interface ExportConditions {
8
+ [key: string]: string | undefined;
9
+ entrypoint?: string;
10
+ types?: string;
11
+ browser?: string;
12
+ import?: string;
13
+ }
14
+
15
+ interface DependencyMap {
16
+ [packageName: string]: string;
17
+ }
18
+
19
+ interface PackageManifest {
20
+ version: string;
21
+ exports?: Record<string, ExportConditions>;
22
+ dependencies?: DependencyMap;
23
+ devDependencies?: DependencyMap;
24
+ peerDependencies?: DependencyMap;
25
+ }
26
+
27
+ function getManifest(packageIdentifier?: string): PackageManifest {
28
+
29
+ const manifestPath = packageIdentifier
30
+ ? join('node_modules', packageIdentifier, 'package.json')
31
+ : 'package.json';
32
+
33
+ let manifest: PackageManifest;
34
+ try {
35
+ manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
36
+ } catch {
37
+ if (packageIdentifier)
38
+ throw new Error(`[scripts/build-bundle.ts] Could not read manifest for '${packageIdentifier}' at '${manifestPath}'.`);
39
+ else
40
+ throw new Error(`[scripts/build-bundle.ts] Could not read package manifest at '${manifestPath}'.`);
41
+ }
42
+
43
+ return manifest;
44
+
45
+ }
46
+
47
+ function getManifestEntrypoints(packageManifest: PackageManifest): string[] {
48
+
49
+ const exports = packageManifest.exports;
50
+ if (!exports)
51
+ throw new Error(`[scripts/build-bundle.ts] No 'exports' field found in the given package manifest.`);
52
+
53
+ const entrypoints = Object.entries(exports)
54
+ .map(([key, conditions]) => {
55
+ if (!conditions.entrypoint)
56
+ throw new Error(`[scripts/build-bundle.ts] Export '${key}' does not have an 'entrypoint' condition.`);
57
+ return conditions.entrypoint;
58
+ });
59
+
60
+ return entrypoints;
61
+
62
+ }
63
+
64
+ function getPackageVersion(manifest: PackageManifest, packageIdentifier?: string): string {
65
+
66
+ let version: string | undefined;
67
+ if (packageIdentifier) {
68
+
69
+ const dependencies = {
70
+ ...manifest.dependencies,
71
+ ...manifest.devDependencies,
72
+ ...manifest.peerDependencies,
73
+ };
74
+
75
+ version = dependencies[packageIdentifier];
76
+ if (!version)
77
+ throw new Error(`[scripts/build-bundle.ts] Package '${packageIdentifier}' is not listed in dependencies.`);
78
+
79
+ }
80
+ else {
81
+ if (!manifest.version)
82
+ throw new Error('[scripts/build-bundle.ts] Package manifest does not contain a version field.');
83
+ version = manifest.version;
84
+ }
85
+
86
+ return version;
87
+
88
+ }
89
+
90
+ function resolveCdnUrl(importSpecifier: string, urlTemplate: string): string {
91
+ const manifest = getManifest();
92
+ const version = getPackageVersion(manifest, importSpecifier);
93
+ return urlTemplate.replace('<VERSION>', version);
94
+ }
95
+
96
+ const cdnRewritePlugin = {
97
+ name: 'cdn-rewrite',
98
+ setup(build: any) {
99
+
100
+ const resolved = new Map<string, string>();
101
+ for (const [importSpecifier, urlTemplate] of Object.entries(CDN_REWRITE_MAP) as [string, string][]) {
102
+ const url = resolveCdnUrl(importSpecifier, urlTemplate);
103
+ resolved.set(importSpecifier, url);
104
+ console.log(`[cdn-rewrite] '${importSpecifier}' → '${url}'`);
105
+ }
106
+
107
+ build.onResolve({ filter: /\*/ }, (args: any) => {
108
+ const url = resolved.get(args.path);
109
+ if (url) return { path: url, external: true };
110
+ });
111
+
112
+ },
113
+ };
114
+
115
+
116
+ const entrypoints = getManifestEntrypoints(getManifest());
117
+ console.log('[scripts/build-bundle.ts] Entrypoints:', entrypoints);
118
+
119
+ let buildResult;
120
+
121
+ console.log('[scripts/build-bundle.ts] Starting ESM bundle build...');
122
+ buildResult = await Bun.build({
123
+ entrypoints,
124
+ outdir: 'dist',
125
+ naming: '[dir]/[name].bundle.[ext]',
126
+ target: 'browser',
127
+ format: 'esm',
128
+ minify: true,
129
+ sourcemap: 'external',
130
+ plugins: [cdnRewritePlugin],
131
+ });
132
+
133
+ if (!buildResult.success) {
134
+ console.error('[scripts/build-bundle.ts] Build failed:');
135
+ for (const message of buildResult.logs) {
136
+ console.error(message);
137
+ }
138
+ process.exit(1);
139
+ }
140
+ console.log('[scripts/build-bundle.ts] ESM bundle build completed successfully.');
141
+
142
+ console.log('[scripts/build-bundle.ts] Starting IIFE bundle build...');
143
+ buildResult = await Bun.build({
144
+ entrypoints,
145
+ outdir: 'dist',
146
+ naming: '[dir]/[name].iife.[ext]',
147
+ target: 'browser',
148
+ format: 'iife',
149
+ minify: true,
150
+ sourcemap: 'external',
151
+ plugins: [cdnRewritePlugin],
152
+ });
153
+
154
+ if (!buildResult.success) {
155
+ console.error('[scripts/build-bundle.ts] Build failed:');
156
+ for (const message of buildResult.logs) {
157
+ console.error(message);
158
+ }
159
+ process.exit(1);
160
+ }
161
+ console.log('[scripts/build-bundle.ts] IIFE bundle build completed successfully.');
@@ -0,0 +1,25 @@
1
+ import { execSync } from 'child_process';
2
+ import { readFileSync, writeFileSync } from 'fs';
3
+
4
+
5
+ const BUILD_INFO_FILE = 'buildinfo.txt';
6
+ const GIT_COMMAND = 'git rev-parse --short HEAD';
7
+
8
+ const version: string = JSON.parse(readFileSync('package.json', 'utf-8')).version;
9
+
10
+ let gitHash = '';
11
+ try {
12
+ gitHash = execSync(GIT_COMMAND, { stdio: ['ignore', 'pipe', 'ignore'] })
13
+ .toString()
14
+ .trim();
15
+ } catch { }
16
+
17
+ const buildinfo = gitHash
18
+ ? version.includes('+')
19
+ ? `${version}.${gitHash}`
20
+ : `${version}+${gitHash}`
21
+ : version;
22
+
23
+ writeFileSync(BUILD_INFO_FILE, buildinfo.trim(), 'utf-8');
24
+
25
+ console.log(`'${BUILD_INFO_FILE}' has been updated with build info: ${buildinfo}`);
@@ -0,0 +1 @@
1
+ {}
@@ -5,9 +5,8 @@
5
5
  ## Table of Contents
6
6
 
7
7
  1. [Quick Start](#quick-start)
8
- 2. [AI Assistant Context](#ai-assistant-context)
9
- 3. [Documentation](#documentation)
10
- 4. [DevOps](#devops)
8
+ 2. [Documentation](#documentation)
9
+ 3. [DevOps](#devops)
11
10
  1. [Change Management](#change-management)
12
11
  2. [Publish](#publish)
13
12
  1. [npmjs.org](#npmjsorg)
@@ -16,13 +15,13 @@
16
15
  # Quick Start
17
16
 
18
17
  ```bash
19
- # remove dist/ and tsconfig.build.tsbuildinfo
18
+ # remove dist/ and tsconfig.tsbuildinfo and tsconfig.build.tsbuildinfo
20
19
  bun run clean
21
20
 
22
21
  # remove dist/ only
23
22
  bun run clean:dist
24
23
 
25
- # remove tsconfig.build.tsbuildinfo only
24
+ # remove tsconfig.tsbuildinfo and tsconfig.build.tsbuildinfo only
26
25
  bun run clean:tsbuildinfo
27
26
 
28
27
  # compile + bundle
@@ -35,30 +34,10 @@ bun run tests
35
34
  bun run dev
36
35
  ```
37
36
 
38
- **Publish** - see [Publish](#publish).
39
-
40
- # AI Assistant Context
41
-
42
- To generate an AI coding assistant context file for this project:
43
-
44
- > Generate an AI coding assistant context file. `README-template.md` is available now and documents the architectural conventions of this package — use it as your primary source. The generated context file must be self-contained: `README-template.md` will be deleted after context generation, so do not reference it in the output. Give particular attention to: the dual tsconfig setup and the import constraints it imposes, the `#src/*.js` alias and where it may and may not be used, the `./spa` export condition and createSpa() as the library's public API, the asset resolution via import.meta.url and why it is reliable when the package is loaded externalized, and the base href patching mechanism for client-side routing.
45
-
46
37
  # Documentation
47
38
 
48
39
  *&lt;DOCUMENTATION&gt;*
49
40
 
50
- ## Asset Resolution
51
-
52
- *&lt;!-- Remove this section if the library has no runtime assets. Replace `@scope/lib-name` throughout with the actual package name. --&gt;*
53
-
54
- Assets are placed under `assets/@scope/lib-name/` (scoped to the package name to prevent collisions). They are resolved at runtime via `import.meta.url` and loaded with `fetch()` (browser) or `readFile` (Node). `assets/` must be listed in `files` in `package.json`.
55
-
56
- `import.meta.url` is reliable when the library is loaded as a discrete module (from CDN or `node_modules/`). When the consumer bundles the library, they must copy the assets - see below.
57
-
58
- ### For consumers bundling this library
59
-
60
- Copy `node_modules/@scope/lib-name/assets/` into your build output alongside the bundle, preserving the relative path. No code configuration is required - only the file copy.
61
-
62
41
  # DevOps
63
42
 
64
43
  ## Change Management
@@ -95,9 +74,9 @@ bun publish --registry https://registry.npmjs.org/ --access public
95
74
 
96
75
  ```bash
97
76
  # placeholder:
98
- # <SCOPE_WITHOUT_AT: <SCOPE_WITHOUT_AT>
99
- # <REGISTRY_URL: <REGISTRY_URL>
100
- # <BUN_PUBLISH_AUTH_TOKEN: <BUN_PUBLISH_AUTH_TOKEN>
77
+ # <SCOPE_WITHOUT_AT: <SCOPE_WITHOUT_AT>
78
+ # <REGISTRY_URL: <REGISTRY_URL>
79
+ # <BUN_PUBLISH_AUTH_TOKEN: <BUN_PUBLISH_AUTH_TOKEN>
101
80
  ```
102
81
 
103
82
  `~/.bunfig.toml` or `bunfig.toml`:
@@ -1,55 +1,55 @@
1
- {
2
- "name": "",
3
- "version": "0.0.0+FE",
4
- "description": "",
5
- "author": "",
6
- "license": "",
7
- "keywords": [
8
- "typescript"
9
- ],
10
- "repository": {
11
- "type": "git",
12
- "url": ""
13
- },
14
- "type": "module",
15
- "exports": {
16
- ".": {
17
- "entrypoint": "./src/index.ts",
18
- "types": "./dist/index.d.ts",
19
- "browser": "./dist/index.bundle.js",
20
- "import": "./dist/index.js"
21
- },
22
- "./spa": {
23
- "entrypoint": "./src/spa.ts",
24
- "types": "./dist/spa.d.ts",
25
- "browser": "./dist/spa.bundle.js",
26
- "import": "./dist/spa.js"
27
- }
28
- },
29
- "imports": {
30
- "#src/*.js": "./src/*.ts"
31
- },
32
- "files": [
33
- "dist",
34
- "CHANGELOG.md",
35
- "buildinfo.txt",
36
- "assets"
37
- ],
38
- "scripts": {
39
- "clean:dist": "rm -rf dist/",
40
- "clean:tsbuildinfo": "rm -f tsconfig.build.tsbuildinfo",
41
- "clean": "bun run clean:dist && bun run clean:tsbuildinfo",
42
- "prebuild": "bun run scripts/buildinfo.ts",
43
- "tests": "bun test",
44
- "build": "bun run build:lib && bun run build:lib-bundle",
45
- "build:lib": "tsc --project tsconfig.build.json",
46
- "build:lib-bundle": "bun run scripts/build-lib-bundle.ts",
47
- "typecheck": "tsc --noEmit",
48
- "dev": "bun run --watch src/dev.ts"
49
- },
50
- "devDependencies": {
51
- "@types/bun": "latest",
52
- "typescript": "^6.0.2",
53
- "hono": "^4.12.5"
54
- }
1
+ {
2
+ "name": "",
3
+ "version": "0.0.0+FE",
4
+ "description": "",
5
+ "author": "",
6
+ "license": "",
7
+ "keywords": [
8
+ "typescript"
9
+ ],
10
+ "repository": {
11
+ "type": "git",
12
+ "url": ""
13
+ },
14
+ "type": "module",
15
+ "exports": {
16
+ ".": {
17
+ "entrypoint": "./src/index.ts",
18
+ "types": "./dist/index.d.ts",
19
+ "browser": "./dist/index.bundle.js",
20
+ "import": "./dist/index.js"
21
+ },
22
+ "./spa": {
23
+ "entrypoint": "./src/spa.ts",
24
+ "types": "./dist/spa.d.ts",
25
+ "browser": "./dist/spa.bundle.js",
26
+ "import": "./dist/spa.js"
27
+ }
28
+ },
29
+ "imports": {
30
+ "#src/*.js": "./src/*.ts"
31
+ },
32
+ "files": [
33
+ "dist",
34
+ "CHANGELOG.md",
35
+ "buildinfo.txt",
36
+ "assets"
37
+ ],
38
+ "scripts": {
39
+ "clean:dist": "rm -rf dist/",
40
+ "clean:tsbuildinfo": "rm -f tsconfig.tsbuildinfo tsconfig.build.tsbuildinfo",
41
+ "clean": "bun run clean:dist && bun run clean:tsbuildinfo",
42
+ "prebuild": "bun run scripts/buildinfo.ts",
43
+ "tests": "bun test",
44
+ "build": "bun run build:lib && bun run build:lib-bundle",
45
+ "build:lib": "tsc --project tsconfig.build.json",
46
+ "build:lib-bundle": "bun run scripts/build-lib-bundle.ts",
47
+ "typecheck": "tsc --noEmit",
48
+ "dev": "bun run --watch src/dev.ts"
49
+ },
50
+ "devDependencies": {
51
+ "@types/bun": "latest",
52
+ "typescript": "^6.0.2",
53
+ "hono": "^4.12.5"
54
+ }
55
55
  }
package/dist/cli.d.ts DELETED
@@ -1,3 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
3
- //# sourceMappingURL=cli.d.ts.map
package/dist/cli.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js DELETED
@@ -1,26 +0,0 @@
1
- #!/usr/bin/env node
2
- import { cpSync, readFileSync, renameSync, writeFileSync } from 'fs';
3
- import { resolve, basename } from 'path';
4
- import { templatePath, changelogPath, buildinfoPath, readmePath } from './constants.js';
5
- try {
6
- const packageNameArgument = process.argv[2];
7
- if (!packageNameArgument)
8
- throw new Error('Package name argument is required. Usage: `create-hono-spa <package-name>`');
9
- const packageName = packageNameArgument.replace(/\\/g, '/');
10
- const destinationPath = resolve(process.cwd(), packageName);
11
- cpSync(templatePath, destinationPath, { recursive: true });
12
- cpSync(changelogPath, resolve(destinationPath, 'CHANGELOG-template.md'));
13
- cpSync(buildinfoPath, resolve(destinationPath, 'buildinfo-template.txt'));
14
- cpSync(readmePath, resolve(destinationPath, 'README-template.md'));
15
- const packageJsonPath = resolve(destinationPath, 'package.json');
16
- const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
17
- packageJson.name = packageName;
18
- writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
19
- renameSync(resolve(destinationPath, 'gitignore'), resolve(destinationPath, '.gitignore'));
20
- console.log(`Template has been successfully instantiated at '${destinationPath}' with package name '${packageName}'.`);
21
- }
22
- catch (error) {
23
- const err = error instanceof Error ? error : new Error(String(error));
24
- console.error('Error:', err.message);
25
- process.exit(1);
26
- }
@@ -1,7 +0,0 @@
1
- export declare const packageUrl: URL;
2
- export declare const packagePath: string;
3
- export declare const templatePath: string;
4
- export declare const changelogPath: string;
5
- export declare const buildinfoPath: string;
6
- export declare const readmePath: string;
7
- //# sourceMappingURL=constants.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,UAAU,EAAE,GAAqC,CAAC;AAC/D,eAAO,MAAM,WAAW,EAAE,MAA2C,CAAC;AAEtE,eAAO,MAAM,YAAY,EAAE,MAA0C,CAAC;AAEtE,eAAO,MAAM,aAAa,EAAE,MAA6C,CAAC;AAC1E,eAAO,MAAM,aAAa,EAAE,MAA8C,CAAC;AAC3E,eAAO,MAAM,UAAU,EAAE,MAA0C,CAAC"}
package/dist/constants.js DELETED
@@ -1,8 +0,0 @@
1
- import { resolve } from 'path';
2
- import { fileURLToPath } from 'url';
3
- export const packageUrl = new URL('../', import.meta.url);
4
- export const packagePath = resolve(fileURLToPath(packageUrl));
5
- export const templatePath = resolve(packagePath, 'template/');
6
- export const changelogPath = resolve(packagePath, 'CHANGELOG.md');
7
- export const buildinfoPath = resolve(packagePath, 'buildinfo.txt');
8
- export const readmePath = resolve(packagePath, 'README.md');