@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 +16 -0
- package/README.md +140 -11
- package/buildinfo.txt +1 -1
- package/dist/cli.bundle.js +2 -2
- package/dist/cli.bundle.js.map +4 -5
- package/dist/cli.js +3 -1
- package/package.json +1 -1
- package/template/AGENTS.md +1 -1
- package/template/CLAUDE.md +158 -132
- package/template/README.md +3 -1
- package/template/src/dev.ts +2 -4
- package/template/tests/buildinfo.test.ts +2 -1
- package/dist/urls.d.ts +0 -5
- package/dist/urls.d.ts.map +0 -1
- package/dist/urls.js +0 -4
- package/template/src/urls.ts +0 -3
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. [
|
|
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"
|
|
188
|
-
// "types"
|
|
189
|
-
// "browser"
|
|
190
|
-
// "import"
|
|
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
|
|
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
|
-
##
|
|
287
|
+
## Asset Resolution
|
|
288
288
|
|
|
289
|
-
|
|
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
|
|
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
|
|
300
|
-
- `template/` pair
|
|
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
|
|
1
|
+
0.2.1+5ddcc14
|
package/dist/cli.bundle.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{cpSync as
|
|
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=
|
|
4
|
+
//# debugId=4F322049A7A4E1DA64756E2164756E21
|
package/dist/cli.bundle.js.map
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["..\\src\\cli.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\
|
|
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,
|
|
9
|
-
"debugId": "
|
|
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
|
-
|
|
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
package/template/AGENTS.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
See [CLAUDE.md](CLAUDE.md).
|
|
1
|
+
See [CLAUDE.md](CLAUDE.md).
|
package/template/CLAUDE.md
CHANGED
|
@@ -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
|
-
│
|
|
12
|
-
|
|
13
|
-
├──
|
|
14
|
-
│ ├──
|
|
15
|
-
│
|
|
16
|
-
|
|
17
|
-
├──
|
|
18
|
-
├──
|
|
19
|
-
├──
|
|
20
|
-
├──
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
bun
|
|
29
|
-
bun run
|
|
30
|
-
bun run
|
|
31
|
-
bun run
|
|
32
|
-
bun run
|
|
33
|
-
bun run clean
|
|
34
|
-
bun run clean:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
| `src
|
|
45
|
-
| `src
|
|
46
|
-
| `
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
import { helper } from "./helper
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
export function greet(name: string)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
import type
|
|
75
|
-
import {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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 scratchpad — excluded from build, never published; use for manual testing |
|
|
46
|
+
| `tests/*.test.ts` | Unit tests — Bun 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` | Build — emits 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.
|
package/template/README.md
CHANGED
|
@@ -32,10 +32,12 @@ bun run tests
|
|
|
32
32
|
bun run dev
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
**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
|
*<DOCUMENTATION>*
|
|
40
42
|
|
|
41
43
|
# DevOps
|
package/template/src/dev.ts
CHANGED
|
@@ -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
|
-
|
|
8
|
-
console.log(`'
|
|
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
|
-
|
|
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
package/dist/urls.d.ts.map
DELETED
|
@@ -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
package/template/src/urls.ts
DELETED