@temir.ra/create-ts-lib 0.1.0 → 0.2.1-patch.2

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,21 @@
1
1
  # Version 0
2
2
 
3
+ ## 0.2.1
4
+
5
+ 1. Added asset resolution documentation to root README: scoped asset folder convention, inline `import.meta.url` resolution, `fetch()` for isomorphic I/O, setup steps, checklist, and consumer README statement.
6
+ 2. Updated template README with a reference to the asset resolution docs in the create-ts-lib README.
7
+ 3. Updated both `CLAUDE.md` files.
8
+ 4. Updated both `dev.ts` files to use inline `new URL('../', import.meta.url)`.
9
+ 5. Removed `src/urls.ts` from root and template (not needed — inline resolution is sufficient).
10
+
11
+ ### patch.1
12
+
13
+ 1. Fixed minor documentation issues.
14
+
15
+ ### patch.2
16
+
17
+ 1. Fixed missing `template/` files in the published package.
18
+
3
19
  ## 0.1.0
4
20
 
5
21
  1. First version of the template.
package/README.md CHANGED
@@ -11,7 +11,7 @@ A template for TypeScript libraries distributed via npm-compatible registries. P
11
11
  3. [Script `scripts/buildinfo.ts`](#script-scriptsbuildinfots)
12
12
  4. [Script `scripts/build-lib-bundle.ts`](#script-scriptsbuild-lib-bundlets)
13
13
  5. [CDN Map `scripts/cdn-rewrite-map.json`](#cdn-map-scriptscdn-rewrite-mapjson)
14
- 6. [`src/urls.ts`](#srcurlsts)
14
+ 6. [Asset Resolution](#asset-resolution)
15
15
  7. [`src/dev.ts`](#srcdevts)
16
16
  8. [`CLAUDE.md` / `AGENTS.md`](#claudemd--agentsmd)
17
17
  3. [DevOps](#devops)
@@ -184,10 +184,10 @@ bun install
184
184
  "sideEffects": false,
185
185
 
186
186
  // package entry points by consumer type
187
- // "entrypoint" custom condition; used by the bundle script to locate the source entry point
188
- // "types" TypeScript consumers; resolves to the declaration files; must precede "import" so TypeScript matches it before the JS condition
189
- // "browser" browser bundler consumers; resolves to the bundled output
190
- // "import" ESM consumers; resolves to the compiled module output
187
+ // "entrypoint" - custom condition; used by the bundle script to locate the source entry point
188
+ // "types" - TypeScript consumers; resolves to the declaration files; must precede "import" so TypeScript matches it before the JS condition
189
+ // "browser" - browser bundler consumers; resolves to the bundled output
190
+ // "import" - ESM consumers; resolves to the compiled module output
191
191
  "exports": {
192
192
  ".": {
193
193
  "entrypoint": "./src/index.ts",
@@ -198,7 +198,7 @@ bun install
198
198
  },
199
199
 
200
200
  // package-internal import alias resolved natively by Node.js and Bun at runtime; key must start with #
201
- // for use in dev.ts, tests/, and scripts/ only NOT in library source files compiled by tsconfig.build.json
201
+ // for use in dev.ts, tests/, and scripts/ only - NOT in library source files compiled by tsconfig.build.json
202
202
  // the .js extension is required in import statements (nodenext compliance);
203
203
  // the runtime maps it to the actual .ts source file via this field
204
204
  "imports": {
@@ -284,20 +284,149 @@ For CLI packages, add the following to `package.json`:
284
284
  }
285
285
  ```
286
286
 
287
- ## `src/urls.ts`
287
+ ## Asset Resolution
288
288
 
289
- Resolves `CHANGELOG.md` and `buildinfo.txt` relative to `dist/` at runtime using `import.meta.url`. Provides stable references to published package assets regardless of install location.
289
+ The key question to ask is: **does my library retain its own URL at runtime?** Module identity is the invariant. Everything else follows from it.
290
+
291
+ ### Externalized - loaded from CDN or node_modules
292
+
293
+ The consumer does not bundle the library. It is loaded as a discrete module at runtime. Module identity is preserved regardless of the URL form:
294
+
295
+ ```
296
+ https://cdn.jsdelivr.net/npm/@scope/lib@1.0.0/dist/index.js
297
+ file:///project/node_modules/@scope/lib/dist/index.js
298
+ https://your-own-cdn.com/lib/index.js
299
+ ```
300
+
301
+ `import.meta.url` is reliable in all these cases. Asset paths resolve automatically - the consumer does nothing. `fetch()` is the correct I/O mechanism because it accepts both `https://` and `file://` URLs, making it universally correct for isomorphic (browser + Node) libraries.
302
+
303
+ ### Bundled into the consumer's app
304
+
305
+ The consumer's bundler absorbs the library into their own output file. Module identity is destroyed - `import.meta.url` now points to the consumer's bundle, not the library's file. However, **the path relationship can be preserved by convention**: if the consumer copies the library's assets into their build output at the same relative path the library expects, `import.meta.url` resolution continues to work correctly.
306
+
307
+ ### Scoped asset folder convention
308
+
309
+ Place all runtime assets under a scoped directory that mirrors the package name:
310
+
311
+ ```
312
+ assets/
313
+ └── @scope/
314
+ └── lib-name/
315
+ ├── schema.json
316
+ └── data.json
317
+ ```
318
+
319
+ This prevents naming collisions when multiple libraries are bundled into the same consumer app. Each library's assets live under their own namespace; no library can shadow another's files.
320
+
321
+ Add `assets/` to the `files` field in `package.json` so npm publishes it:
322
+
323
+ ```json
324
+ "files": [
325
+ "dist",
326
+ "CHANGELOG.md",
327
+ "buildinfo.txt",
328
+ "assets"
329
+ ],
330
+ ```
331
+
332
+ ### Accessing assets
333
+
334
+ Construct asset URLs directly from `import.meta.url`. Replace `@scope/lib-name` with your actual package name:
335
+
336
+ ```typescript
337
+ const packageUrl = new URL('../', import.meta.url);
338
+ const assetsUrl = new URL('assets/@scope/lib-name/', packageUrl);
339
+
340
+ // isomorphic (browser or Node) - fetch accepts both https:// and file:// URLs
341
+ const schema = await fetch(new URL('schema.json', assetsUrl)).then(r => r.json());
342
+
343
+ // server-only (Node/Bun)
344
+ import { readFile } from 'node:fs/promises';
345
+ import { fileURLToPath } from 'url';
346
+ const data = JSON.parse(await readFile(fileURLToPath(new URL('data.json', assetsUrl)), 'utf-8'));
347
+ ```
348
+
349
+ `fetch()` is the correct I/O primitive for isomorphic libraries: it accepts both `https://` URLs (CDN/production) and `file://` URLs (node_modules/development) without any special handling.
350
+
351
+ > **Server-only libraries** (no browser target, bundled-into-consumer scenario unlikely): use `readFile` directly - `fetch()` is not required. The scoped asset convention still applies unchanged.
352
+
353
+ ### When the library is bundled by the consumer
354
+
355
+ The consumer must copy the assets into their build output at the same relative path the library expects:
356
+
357
+ ```
358
+ consumer output/
359
+ ├── bundle.js ← import.meta.url points here
360
+ └── assets/
361
+ └── @scope/
362
+ └── lib-name/
363
+ ├── schema.json ← copied; relative path preserved
364
+ └── data.json
365
+ ```
366
+
367
+ From the bundle's perspective `assets/@scope/lib-name/` is at the same directory level as `bundle.js`, so `new URL('assets/@scope/lib-name/schema.json', import.meta.url)` resolves correctly. No code configuration is needed - only the file copy.
368
+
369
+ ### Setup
370
+
371
+ If your library has runtime assets, the steps to wire everything up:
372
+
373
+ 1. **Create the scoped directory** `assets/@scope/lib-name/` and place asset files inside (replace `@scope/lib-name` with your actual package name).
374
+
375
+ 2. **Access assets via `import.meta.url`** - see [Accessing assets](#accessing-assets) above.
376
+
377
+ 3. **Publish the assets** - add `assets/` to the `files` field in `package.json`:
378
+ ```json
379
+ "files": [
380
+ "dist",
381
+ "CHANGELOG.md",
382
+ "buildinfo.txt",
383
+ "assets"
384
+ ],
385
+ ```
386
+
387
+ 4. **Document the bundled case** - copy the [README statement](#readme-statement-for-your-consumers) into your library's README.
388
+
389
+ ### Contract
390
+
391
+ **The library must:**
392
+ - Place assets under `assets/@scope/lib-name/` (mirroring the package name)
393
+ - Resolve asset paths via `import.meta.url` - no shared helper file required
394
+ - Include `assets/` in `package.json` `files`
395
+ - Document the bundled-case copy requirement in its README
396
+
397
+ **The consumer must, when bundling the library:**
398
+ - Copy `node_modules/@scope/lib-name/assets/` into their build output alongside the bundle
399
+ - Ensure assets are served
400
+
401
+ **The consumer must do nothing when loading the library externalized.** Asset resolution is automatic.
402
+
403
+ ### Checklist
404
+
405
+ - [ ] Assets live under `assets/@scope/lib-name/` (matching the package name)
406
+ - [ ] `assets/` is listed in `package.json` `files`
407
+ - [ ] All asset access resolves paths via `import.meta.url` - no hardcoded paths
408
+ - [ ] Library README documents the bundled-case copy requirement (see statement below)
409
+
410
+ ### README statement for your consumers
411
+
412
+ Copy this into your library's README:
413
+
414
+ > **Asset resolution**
415
+ >
416
+ > This library resolves assets at runtime using `import.meta.url`. It works automatically when the library is loaded as an external module (from CDN or node_modules).
417
+ >
418
+ > If you bundle this library into your application, copy `node_modules/@scope/lib-name/assets/@scope/lib-name/` into your build output directory alongside your bundle. No code configuration is required.
290
419
 
291
420
  ## `src/dev.ts`
292
421
 
293
- Development scratchpad not published, excluded from `tsconfig.build.json`. Run with `bun run dev` in watch mode. Use it to manually test and explore library code during development.
422
+ Development scratchpad - not published, excluded from `tsconfig.build.json`. Run with `bun run dev` in watch mode. Use it to manually test and explore library code during development.
294
423
 
295
424
  ## `CLAUDE.md` / `AGENTS.md`
296
425
 
297
426
  AI assistant context files. Provide project layout, commands, and architecture notes. `AGENTS.md` references `CLAUDE.md`. The template ships two pairs:
298
427
 
299
- - Root pair describes the template package itself (scaffolding tool, CLI, how `template/` maps to generated projects)
300
- - `template/` pair describes the generated library project; update to reflect the specific library being developed
428
+ - Root pair - describes the template package itself (scaffolding tool, CLI, how `template/` maps to generated projects)
429
+ - `template/` pair - describes the generated library project; update to reflect the specific library being developed
301
430
 
302
431
  # DevOps
303
432
 
package/buildinfo.txt CHANGED
@@ -1 +1 @@
1
- 0.1.0+d78d3df
1
+ 0.2.1+5ddcc14
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import{cpSync as o,readFileSync as U,renameSync as f,writeFileSync as d}from"fs";import{resolve as t,basename as u}from"path";import{fileURLToPath as a}from"url";var n=new URL(".",import.meta.url),l=new URL("../CHANGELOG.md",n),p=new URL("../buildinfo.txt",n),m=new URL("../template",n);try{let r=process.argv[2];if(!r)throw Error("Destination path is required. Usage: `bun/npm create <template> <destination>`");let e=t(process.cwd(),r),i=u(e),g=t(a(m));o(g,e,{recursive:!0}),o(t(a(l)),t(e,"CHANGELOG-template.md")),o(t(a(p)),t(e,"buildinfo-template.txt"));let s=t(e,"package.json"),c=JSON.parse(U(s,"utf-8"));c.name=i,d(s,JSON.stringify(c,null,2)),f(t(e,"gitignore"),t(e,".gitignore")),console.log(`Template has been successfully instantiated at '${e}' with package name '${i}'.`)}catch(r){let e=r instanceof Error?r:Error(String(r));console.error("Error:",e.message),process.exit(1)}
2
+ import{cpSync as r,readFileSync as m,renameSync as p,writeFileSync as g}from"fs";import{resolve as t,basename as f}from"path";import{fileURLToPath as a}from"url";var o=new URL("../template/",import.meta.url),d=new URL("CHANGELOG.md",o),u=new URL("buildinfo.txt",o);try{let n=process.argv[2];if(!n)throw Error("Destination path is required. Usage: `bun/npm create <template> <destination>`");let e=t(process.cwd(),n),s=f(e),l=t(a(o));r(l,e,{recursive:!0}),r(t(a(d)),t(e,"CHANGELOG-template.md")),r(t(a(u)),t(e,"buildinfo-template.txt"));let i=t(e,"package.json"),c=JSON.parse(m(i,"utf-8"));c.name=s,g(i,JSON.stringify(c,null,2)),p(t(e,"gitignore"),t(e,".gitignore")),console.log(`Template has been successfully instantiated at '${e}' with package name '${s}'.`)}catch(n){let e=n instanceof Error?n:Error(String(n));console.error("Error:",e.message),process.exit(1)}
3
3
 
4
- //# debugId=683E20367A2ED95764756E2164756E21
4
+ //# debugId=4F322049A7A4E1DA64756E2164756E21
@@ -1,11 +1,10 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["..\\src\\cli.ts", "..\\src\\urls.ts"],
3
+ "sources": ["..\\src\\cli.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 { fileURLToPath } from 'url';\r\nimport { buildinfoUrl, changelogUrl, templateUrl } from '#src/urls.js';\r\n\r\n\r\ntry {\r\n\r\n const dest = process.argv[2];\r\n if (!dest) throw new Error('Destination path is required. Usage: `bun/npm create <template> <destination>`');\r\n\r\n const destinationPath = resolve(process.cwd(), dest);\r\n const packageName = basename(destinationPath);\r\n\r\n const templatePath = resolve(fileURLToPath(templateUrl));\r\n\r\n cpSync(templatePath, destinationPath, { recursive: true });\r\n cpSync(resolve(fileURLToPath(changelogUrl)), resolve(destinationPath, 'CHANGELOG-template.md'));\r\n cpSync(resolve(fileURLToPath(buildinfoUrl)), resolve(destinationPath, 'buildinfo-template.txt'));\r\n\r\n const packageManifestPath = resolve(destinationPath, 'package.json');\r\n const packageManifest = JSON.parse(readFileSync(packageManifestPath, 'utf-8'));\r\n packageManifest.name = packageName;\r\n writeFileSync(packageManifestPath, JSON.stringify(packageManifest, 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
- "export const distUrl: URL = new URL('.', import.meta.url);\r\nexport const changelogUrl: URL = new URL('../CHANGELOG.md', distUrl);\r\nexport const buildinfoUrl: URL = new URL('../buildinfo.txt', distUrl);\r\nexport const templateUrl: URL = new URL('../template', distUrl);\r\n"
5
+ "#!/usr/bin/env node\r\n\r\nimport { cpSync, readFileSync, renameSync, writeFileSync } from 'fs';\r\nimport { resolve, basename } from 'path';\r\nimport { fileURLToPath } from 'url';\r\n\r\n\r\nconst templateUrl = new URL('../template/', import.meta.url);\r\nconst changelogUrl = new URL('CHANGELOG.md', templateUrl);\r\nconst buildinfoUrl = new URL('buildinfo.txt', templateUrl);\r\n\r\n\r\ntry {\r\n\r\n const dest = process.argv[2];\r\n if (!dest) throw new Error('Destination path is required. Usage: `bun/npm create <template> <destination>`');\r\n\r\n const destinationPath = resolve(process.cwd(), dest);\r\n const packageName = basename(destinationPath);\r\n\r\n const templatePath = resolve(fileURLToPath(templateUrl));\r\n\r\n cpSync(templatePath, destinationPath, { recursive: true });\r\n cpSync(resolve(fileURLToPath(changelogUrl)), resolve(destinationPath, 'CHANGELOG-template.md'));\r\n cpSync(resolve(fileURLToPath(buildinfoUrl)), resolve(destinationPath, 'buildinfo-template.txt'));\r\n\r\n const packageManifestPath = resolve(destinationPath, 'package.json');\r\n const packageManifest = JSON.parse(readFileSync(packageManifestPath, 'utf-8'));\r\n packageManifest.name = packageName;\r\n writeFileSync(packageManifestPath, JSON.stringify(packageManifest, 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"
7
6
  ],
8
- "mappings": ";AAEA,iBAAS,kBAAQ,gBAAc,mBAAY,WAC3C,kBAAS,cAAS,aAClB,wBAAS,YCJF,IAAM,EAAe,IAAI,IAAI,IAAK,YAAY,GAAG,EAC3C,EAAoB,IAAI,IAAI,kBAAmB,CAAO,EACtD,EAAoB,IAAI,IAAI,mBAAoB,CAAO,EACvD,EAAmB,IAAI,IAAI,cAAe,CAAO,EDK9D,GAAI,CAEA,IAAM,EAAO,QAAQ,KAAK,GAC1B,GAAI,CAAC,EAAM,MAAU,MAAM,gFAAgF,EAE3G,IAAM,EAAkB,EAAQ,QAAQ,IAAI,EAAG,CAAI,EAC7C,EAAc,EAAS,CAAe,EAEtC,EAAe,EAAQ,EAAc,CAAW,CAAC,EAEvD,EAAO,EAAc,EAAiB,CAAE,UAAW,EAAK,CAAC,EACzD,EAAO,EAAQ,EAAc,CAAY,CAAC,EAAG,EAAQ,EAAiB,uBAAuB,CAAC,EAC9F,EAAO,EAAQ,EAAc,CAAY,CAAC,EAAG,EAAQ,EAAiB,wBAAwB,CAAC,EAE/F,IAAM,EAAsB,EAAQ,EAAiB,cAAc,EAC7D,EAAkB,KAAK,MAAM,EAAa,EAAqB,OAAO,CAAC,EAC7E,EAAgB,KAAO,EACvB,EAAc,EAAqB,KAAK,UAAU,EAAiB,KAAM,CAAC,CAAC,EAE3E,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": "683E20367A2ED95764756E2164756E21",
7
+ "mappings": ";AAEA,iBAAS,kBAAQ,gBAAc,mBAAY,WAC3C,kBAAS,cAAS,aAClB,wBAAS,YAGT,IAAM,EAAc,IAAI,IAAI,eAAgB,YAAY,GAAG,EACrD,EAAe,IAAI,IAAI,eAAgB,CAAW,EAClD,EAAe,IAAI,IAAI,gBAAiB,CAAW,EAGzD,GAAI,CAEA,IAAM,EAAO,QAAQ,KAAK,GAC1B,GAAI,CAAC,EAAM,MAAU,MAAM,gFAAgF,EAE3G,IAAM,EAAkB,EAAQ,QAAQ,IAAI,EAAG,CAAI,EAC7C,EAAc,EAAS,CAAe,EAEtC,EAAe,EAAQ,EAAc,CAAW,CAAC,EAEvD,EAAO,EAAc,EAAiB,CAAE,UAAW,EAAK,CAAC,EACzD,EAAO,EAAQ,EAAc,CAAY,CAAC,EAAG,EAAQ,EAAiB,uBAAuB,CAAC,EAC9F,EAAO,EAAQ,EAAc,CAAY,CAAC,EAAG,EAAQ,EAAiB,wBAAwB,CAAC,EAE/F,IAAM,EAAsB,EAAQ,EAAiB,cAAc,EAC7D,EAAkB,KAAK,MAAM,EAAa,EAAqB,OAAO,CAAC,EAC7E,EAAgB,KAAO,EACvB,EAAc,EAAqB,KAAK,UAAU,EAAiB,KAAM,CAAC,CAAC,EAE3E,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",
8
+ "debugId": "4F322049A7A4E1DA64756E2164756E21",
10
9
  "names": []
11
10
  }
package/dist/cli.js CHANGED
@@ -2,7 +2,9 @@
2
2
  import { cpSync, readFileSync, renameSync, writeFileSync } from 'fs';
3
3
  import { resolve, basename } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
- import { buildinfoUrl, changelogUrl, templateUrl } from '#src/urls.js';
5
+ const templateUrl = new URL('../template/', import.meta.url);
6
+ const changelogUrl = new URL('CHANGELOG.md', templateUrl);
7
+ const buildinfoUrl = new URL('buildinfo.txt', templateUrl);
6
8
  try {
7
9
  const dest = process.argv[2];
8
10
  if (!dest)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@temir.ra/create-ts-lib",
3
- "version": "0.1.0",
3
+ "version": "0.2.1-patch.2",
4
4
  "description": "Typescript library template",
5
5
  "author": "temir.ra",
6
6
  "license": "MIT",
@@ -1 +1 @@
1
- See [CLAUDE.md](CLAUDE.md).
1
+ See [CLAUDE.md](CLAUDE.md).
@@ -1,132 +1,158 @@
1
- # <package-name>
2
-
3
- A Bun TypeScript library.
4
-
5
- ## Project Layout
6
-
7
- ```
8
- <package-name>/
9
- ├── src/
10
- │ ├── index.ts # Library entry point (public API)
11
- ├── dev.ts # Development scratchpad — run with `bun run dev`
12
- │ └── urls.ts # URL helpers (changelog, buildinfo)
13
- ├── scripts/
14
- │ ├── buildinfo.ts # Writes version + git hash to buildinfo.txt
15
- ├── build-lib-bundle.ts # Bundles the library for ESM + CDN distribution
16
- │ └── cdn-rewrite-map.json # CDN import rewrite rules (empty by default)
17
- ├── tests/ # Unit tests (Bun test runner)
18
- ├── dist/ # Build output (gitignored)
19
- ├── buildinfo.txt # Written during build; included in published package
20
- ├── CHANGELOG.md # Version history
21
- ├── CLAUDE.md # This file — AI context for the library project
22
- └── AGENTS.md # References CLAUDE.md
23
- ```
24
-
25
- ## Commands
26
-
27
- ```bash
28
- bun install # Install dependencies
29
- bun run build # Compile + bundle the library
30
- bun run tests # Run unit tests
31
- bun run typecheck # Type-check without emitting
32
- bun run dev # Run src/dev.ts in watch mode
33
- bun run clean # Remove dist/ and tsconfig.build.tsbuildinfo
34
- bun run clean:dist # Remove dist/ only
35
- bun run clean:tsbuildinfo # Remove tsconfig.build.tsbuildinfo only
36
- ```
37
-
38
- ## Writing Code
39
-
40
- ### Where things go
41
-
42
- | Path | Purpose |
43
- |------|---------|
44
- | `src/index.ts` | Public APIthe only entry point consumers see; export everything public from here |
45
- | `src/*.ts` | Library source modules add new modules here, re-export from `index.ts` |
46
- | `src/dev.ts` | Development scratchpadexcluded from build, never published; use for manual testing |
47
- | `tests/*.test.ts` | Unit tests — Bun test runner |
48
-
49
- ### Imports within `src/`
50
-
51
- `tsconfig.build.json` uses `moduleResolution: nodenext`, so imports between library source files must use explicit `.js` extensions:
52
-
53
- ```ts
54
- import { helper } from "./helper.js" //
55
- import { helper } from "./helper" // ✗ — fails under nodenext
56
- ```
57
-
58
- The `#src/*.js` package alias is available in `dev.ts`, `tests/`, and `scripts/`, but **not** in library source files — tsc emits the specifier verbatim and it would break in published unbundled output.
59
-
60
- ### Explicit return types (`isolatedDeclarations`)
61
-
62
- All exported declarations require explicit type annotations so tsc can emit `.d.ts` files without cross-file inference:
63
-
64
- ```ts
65
- export function greet(name: string): string { ... } //
66
- export function greet(name: string) { ... } // ✗ — implicit return type
67
- ```
68
-
69
- ### Type-only imports (`verbatimModuleSyntax`)
70
-
71
- Use `import type` for imports that are only used as types:
72
-
73
- ```ts
74
- import type { Foo } from "./foo.js" // ✓ — type-only import
75
- import { type Foo, bar } from "./foo.js" // mixed
76
- import { Foo } from "./foo.js" // ✗ — if Foo is only used as a type
77
- ```
78
-
79
- ### Tests
80
-
81
- Tests live in `tests/` and use Bun's built-in runner:
82
-
83
- ```ts
84
- import { describe, test, expect } from "bun:test"
85
-
86
- describe("greet", () => {
87
- test("returns greeting", () => {
88
- expect(greet("world")).toBe("hello, world")
89
- })
90
- })
91
- ```
92
-
93
- Run with `bun run tests`.
94
-
95
- ## TypeScript Configuration
96
-
97
- Two tsconfig files serve different purposes:
98
-
99
- | File | Purpose |
100
- |------|---------|
101
- | `tsconfig.json` | Development — no emit, includes `src/`, `tests/`, `scripts/` |
102
- | `tsconfig.build.json` | Build — emits JS + `.d.ts`, `src/` only (excludes `dev.ts`, tests, scripts) |
103
-
104
- `bun run build:lib` uses `tsconfig.build.json`. During development the runtime handles TypeScript directly so no compilation step is needed.
105
-
106
- Both configs enable an extended strict type-safety profile beyond `strict`: `verbatimModuleSyntax`, `noUncheckedIndexedAccess`, `exactOptionalPropertyTypes`, `noImplicitOverride`, `isolatedDeclarations`.
107
-
108
- ## Build Outputs
109
-
110
- `bun run build` runs two steps in sequence:
111
-
112
- 1. **`build:lib`** `tsc --project tsconfig.build.json` → `dist/*.js` + `dist/*.d.ts`
113
- 2. **`build:lib-bundle`** — `scripts/build-lib-bundle.ts` → `dist/index.bundle.js` (ESM, minified) + `dist/index.iife.js` (IIFE, minified)
114
-
115
- The `exports` field in `package.json` maps consumers to the right file:
116
-
117
- | Condition | File |
118
- |-----------|------|
119
- | `types` | `dist/index.d.ts` |
120
- | `browser` | `dist/index.bundle.js` |
121
- | `import` | `dist/index.js` |
122
-
123
- ## Package Configuration
124
-
125
- Key `package.json` fields:
126
-
127
- - `"sideEffects": false` — enables full tree-shaking by bundlers
128
- - `"imports": { "#src/*.js": "./src/*.ts" }` package-internal alias for `src/`; use as `import { foo } from "#src/module.js"`. **Only for `dev.ts`, `tests/`, and `scripts/`** — not for library source files compiled by `tsconfig.build.json`, as tsc emits the specifier verbatim and it would break in published unbundled output.
129
-
130
- ## Publishing
131
-
132
- See [README.md](README.md) for registry setup and publish commands.
1
+ # <package-name>
2
+
3
+ A Bun TypeScript library.
4
+
5
+ ## Project Layout
6
+
7
+ ```
8
+ <package-name>/
9
+ ├── src/
10
+ │ ├── index.ts # Library entry point (public API)
11
+ └── dev.ts # Development scratchpad — run with `bun run dev`
12
+ ├── scripts/
13
+ ├── buildinfo.ts # Writes version + git hash to buildinfo.txt
14
+ │ ├── build-lib-bundle.ts # Bundles the library for ESM + CDN distribution
15
+ └── cdn-rewrite-map.json # CDN import rewrite rules (empty by default)
16
+ ├── tests/ # Unit tests (Bun test runner)
17
+ ├── dist/ # Build output (gitignored)
18
+ ├── buildinfo.txt # Written during build; included in published package
19
+ ├── CHANGELOG.md # Version history
20
+ ├── CLAUDE.md # This file — AI context for the library project
21
+ └── AGENTS.md # References CLAUDE.md
22
+ ```
23
+
24
+ ## Commands
25
+
26
+ ```bash
27
+ bun install # Install dependencies
28
+ bun run build # Compile + bundle the library
29
+ bun run tests # Run unit tests
30
+ bun run typecheck # Type-check without emitting
31
+ bun run dev # Run src/dev.ts in watch mode
32
+ bun run clean # Remove dist/ and tsconfig.build.tsbuildinfo
33
+ bun run clean:dist # Remove dist/ only
34
+ bun run clean:tsbuildinfo # Remove tsconfig.build.tsbuildinfo only
35
+ ```
36
+
37
+ ## Writing Code
38
+
39
+ ### Where things go
40
+
41
+ | Path | Purpose |
42
+ |------|---------|
43
+ | `src/index.ts` | Public API — the only entry point consumers see; export everything public from here |
44
+ | `src/*.ts` | Library source modules add new modules here, re-export from `index.ts` |
45
+ | `src/dev.ts` | Development scratchpadexcluded from build, never published; use for manual testing |
46
+ | `tests/*.test.ts` | Unit testsBun test runner |
47
+
48
+ ### Imports within `src/`
49
+
50
+ `tsconfig.build.json` uses `moduleResolution: nodenext`, so imports between library source files must use explicit `.js` extensions:
51
+
52
+ ```ts
53
+ import { helper } from "./helper.js" // ✓
54
+ import { helper } from "./helper" // ✗ — fails under nodenext
55
+ ```
56
+
57
+ The `#src/*.js` package alias is available in `dev.ts`, `tests/`, and `scripts/`, but **not** in library source files — tsc emits the specifier verbatim and it would break in published unbundled output.
58
+
59
+ ### Explicit return types (`isolatedDeclarations`)
60
+
61
+ All exported declarations require explicit type annotations so tsc can emit `.d.ts` files without cross-file inference:
62
+
63
+ ```ts
64
+ export function greet(name: string): string { ... } // ✓
65
+ export function greet(name: string) { ... } // ✗ — implicit return type
66
+ ```
67
+
68
+ ### Type-only imports (`verbatimModuleSyntax`)
69
+
70
+ Use `import type` for imports that are only used as types:
71
+
72
+ ```ts
73
+ import type { Foo } from "./foo.js" // ✓ — type-only import
74
+ import { type Foo, bar } from "./foo.js" // ✓ — mixed
75
+ import { Foo } from "./foo.js" // if Foo is only used as a type
76
+ ```
77
+
78
+ ### Asset Resolution (if the library has runtime assets)
79
+
80
+ Place runtime assets (JSON data, WASM, binary files, etc.) under a scoped directory that mirrors the package name — this prevents naming collisions when multiple libraries are bundled into the same consumer app:
81
+
82
+ ```
83
+ assets/@scope/lib-name/
84
+ ├── data.json
85
+ └── model.wasm
86
+ ```
87
+
88
+ Resolve asset URLs inline from `import.meta.url` — no shared helper file:
89
+
90
+ ```typescript
91
+ const packageUrl = new URL('../', import.meta.url);
92
+ const assetsUrl = new URL('assets/@scope/lib-name/', packageUrl);
93
+
94
+ // isomorphic (browser + Node) — fetch() handles both https:// and file:// URLs
95
+ const data = await fetch(new URL('data.json', assetsUrl)).then(r => r.json());
96
+
97
+ // server-only (Node/Bun)
98
+ import { readFile } from 'node:fs/promises';
99
+ import { fileURLToPath } from 'url';
100
+ const raw = await readFile(fileURLToPath(new URL('data.json', assetsUrl)), 'utf-8');
101
+ ```
102
+
103
+ Add `assets/` to `files` in `package.json` so it is included in the published package. See the [create-ts-lib README](../README.md) Asset Resolution section for the full setup guide, checklist, and the README statement to copy for your consumers.
104
+
105
+ ### Tests
106
+
107
+ Tests live in `tests/` and use Bun's built-in runner:
108
+
109
+ ```ts
110
+ import { describe, test, expect } from "bun:test"
111
+
112
+ describe("greet", () => {
113
+ test("returns greeting", () => {
114
+ expect(greet("world")).toBe("hello, world")
115
+ })
116
+ })
117
+ ```
118
+
119
+ Run with `bun run tests`.
120
+
121
+ ## TypeScript Configuration
122
+
123
+ Two tsconfig files serve different purposes:
124
+
125
+ | File | Purpose |
126
+ |------|---------|
127
+ | `tsconfig.json` | Development no emit, includes `src/`, `tests/`, `scripts/` |
128
+ | `tsconfig.build.json` | Buildemits JS + `.d.ts`, `src/` only (excludes `dev.ts`, tests, scripts) |
129
+
130
+ `bun run build:lib` uses `tsconfig.build.json`. During development the runtime handles TypeScript directly so no compilation step is needed.
131
+
132
+ Both configs enable an extended strict type-safety profile beyond `strict`: `verbatimModuleSyntax`, `noUncheckedIndexedAccess`, `exactOptionalPropertyTypes`, `noImplicitOverride`, `isolatedDeclarations`.
133
+
134
+ ## Build Outputs
135
+
136
+ `bun run build` runs two steps in sequence:
137
+
138
+ 1. **`build:lib`** — `tsc --project tsconfig.build.json` → `dist/*.js` + `dist/*.d.ts`
139
+ 2. **`build:lib-bundle`** — `scripts/build-lib-bundle.ts` → `dist/index.bundle.js` (ESM, minified) + `dist/index.iife.js` (IIFE, minified)
140
+
141
+ The `exports` field in `package.json` maps consumers to the right file:
142
+
143
+ | Condition | File |
144
+ |-----------|------|
145
+ | `types` | `dist/index.d.ts` |
146
+ | `browser` | `dist/index.bundle.js` |
147
+ | `import` | `dist/index.js` |
148
+
149
+ ## Package Configuration
150
+
151
+ Key `package.json` fields:
152
+
153
+ - `"sideEffects": false` — enables full tree-shaking by bundlers
154
+ - `"imports": { "#src/*.js": "./src/*.ts" }` — package-internal alias for `src/`; use as `import { foo } from "#src/module.js"`. **Only for `dev.ts`, `tests/`, and `scripts/`** — not for library source files compiled by `tsconfig.build.json`, as tsc emits the specifier verbatim and it would break in published unbundled output.
155
+
156
+ ## Publishing
157
+
158
+ See [README.md](README.md) for registry setup and publish commands.
@@ -32,10 +32,12 @@ bun run tests
32
32
  bun run dev
33
33
  ```
34
34
 
35
- **Publish** see [Publish](#publish).
35
+ **Publish** - see [Publish](#publish).
36
36
 
37
37
  # Documentation
38
38
 
39
+ > If this library has runtime assets, see the **Asset Resolution** section in the create-ts-lib README for the setup guide, code examples, checklist, and consumer README statement.
40
+
39
41
  *&lt;DOCUMENTATION&gt;*
40
42
 
41
43
  # DevOps
@@ -1,12 +1,10 @@
1
- import { buildinfoUrl, changelogUrl, distUrl } from '#src/urls.js';
2
1
  import { fileURLToPath } from 'url';
3
2
 
4
3
 
5
4
  const start: number = performance.now();
6
5
 
7
- console.log(`'distPath':`, fileURLToPath(distUrl));
8
- console.log(`'buildinfoPath':`, fileURLToPath(buildinfoUrl));
9
- console.log(`'changelogPath':`, fileURLToPath(changelogUrl));
6
+ const packageUrl = new URL('../', import.meta.url);
7
+ console.log(`'packagePath':`, fileURLToPath(packageUrl));
10
8
 
11
9
 
12
10
  // dev code
@@ -1,7 +1,8 @@
1
1
  import { describe, it, expect } from 'bun:test';
2
2
  import { readFileSync } from 'fs';
3
3
  import { fileURLToPath } from 'url';
4
- import { buildinfoUrl } from '#src/urls.js';
4
+
5
+ const buildinfoUrl = new URL('../buildinfo.txt', import.meta.url);
5
6
 
6
7
 
7
8
  describe('buildInfo', () => {
package/dist/urls.d.ts DELETED
@@ -1,5 +0,0 @@
1
- export declare const distUrl: URL;
2
- export declare const changelogUrl: URL;
3
- export declare const buildinfoUrl: URL;
4
- export declare const templateUrl: URL;
5
- //# sourceMappingURL=urls.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"urls.d.ts","sourceRoot":"","sources":["../src/urls.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,EAAE,GAAmC,CAAC;AAC1D,eAAO,MAAM,YAAY,EAAE,GAAyC,CAAC;AACrE,eAAO,MAAM,YAAY,EAAE,GAA0C,CAAC;AACtE,eAAO,MAAM,WAAW,EAAE,GAAqC,CAAC"}
package/dist/urls.js DELETED
@@ -1,4 +0,0 @@
1
- export const distUrl = new URL('.', import.meta.url);
2
- export const changelogUrl = new URL('../CHANGELOG.md', distUrl);
3
- export const buildinfoUrl = new URL('../buildinfo.txt', distUrl);
4
- export const templateUrl = new URL('../template', distUrl);
@@ -1,3 +0,0 @@
1
- export const distUrl: URL = new URL('.', import.meta.url);
2
- export const changelogUrl: URL = new URL('../CHANGELOG.md', distUrl);
3
- export const buildinfoUrl: URL = new URL('../buildinfo.txt', distUrl);