susee 1.0.4 → 1.5.1

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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
  <!-- markdownlint-disable MD041 -->
3
3
  <div align="center">
4
4
  <img src="https://susee.phothin.dev/logo/susee.webp" width="160" height="160" alt="susee" />
5
- <h1>susee</h1>
5
+ <h1>Susee</h1>
6
6
  </div>
7
7
  <!-- markdownlint-enable MD033 -->
8
8
 
@@ -10,74 +10,54 @@
10
10
 
11
11
  ## Overview
12
12
 
13
- `susee` is a TypeScript bundler that processes a package's local dependency tree and emits compiled artifacts in multiple module formats. Unlike general-purpose bundlers that target browser environments or bundle `node_modules`, `susee` focuses on library authorship: it collates local TypeScript files, merges them into cohesive bundles, compiles them through the TypeScript compiler, and generates properly formatted outputs for consumption as npm packages.
13
+ Susee is a TypeScript-first bundler for library packages.
14
14
 
15
- ## Key Features
15
+ It reads `susee.config.{ts,js,mjs}`, resolves the dependency graph for each entry,
16
+ bundles sources into a single unit, then compiles output for ESM and/or CommonJS with types.
16
17
 
17
- 1. **Dependency Tree Resolution** : automatically resolves and collects local TypeScript dependencies starting from specified entry points.
18
+ ## Features
18
19
 
19
- 2. **Dual-Format Module Output** : generates outputs for both ESM and CommonJS module systems from a single TypeScript source.
20
+ - Loads config from one of:
21
+ - `susee.config.ts`
22
+ - `susee.config.js`
23
+ - `susee.config.mjs`
24
+ - Supports multiple entry points with subpath exports (`.` and `./subpath`).
25
+ - Validates and type-checks dependency files before bundling.
26
+ - Runs dependency, pre-process, and post-process plugins (sync or async).
27
+ - Compiles to:
28
+ - ESM (`.mjs`, `.d.mts`, source maps)
29
+ - CommonJS (`.cjs`, `.d.cts`, source maps)
30
+ - Can update `package.json` fields (`type`, `main`, `module`, `types`, `exports`) when `allowUpdatePackageJson` is enabled.
20
31
 
21
- 3. **File Extension Conventions** : dual-emit conventions for unambiguous module format identification.
32
+ ## Current Constraints
22
33
 
23
- | Module Format | JavaScript Extension | Type Definition Extension |
24
- | ------------- | -------------------- | ------------------------- |
25
- | ESM | `.mjs` | `.d.mts` |
26
- | CommonJS | `.cjs` | `.d.cts` |
34
+ - CommonJS dependencies in the source graph are rejected by core (suggested workaround: `@suseejs/plugin-commonjs`).
35
+ - JSX/TSX dependencies are currently rejected.
36
+ - CLI supports only `susee` and `susee init`.
27
37
 
28
- 4. **Automatic package.json Management** : conditionally updates package.json fields based on compilation outputs, this feature is controlled by the `allowUpdatePackageJson` boolean in `SuSeeConfig`.
38
+ ## Installation and Quick Start
29
39
 
30
- ## Installation
40
+ ### Install
31
41
 
32
- Install as a development dependency :
33
-
34
- ```bash
35
- npm install susee --save-dev
42
+ ```sh
43
+ npm i -D susee
36
44
  ```
37
45
 
38
- Global install:
46
+ ### Create a config file
39
47
 
40
- ```bash
41
- npm install -g susee
48
+ ```sh
49
+ npx susee init
42
50
  ```
43
51
 
44
- ## Quick Start
45
-
46
- The `susee` CLI binary is exposed through the `bin` field and becomes available immediately after installation.
52
+ ### Build your first project
47
53
 
48
- ### Creating a Minimal Configuration
54
+ Use the CLI:
49
55
 
50
- Create a file named `susee.config.ts` in your project root:
51
-
52
- ```ts
53
- import type { SuSeeConfig } from "susee";
54
-
55
- const config: SuSeeConfig = {
56
- entryPoints: [
57
- {
58
- entry: "src/index.ts",
59
- exportPath: ".",
60
- format: "both",
61
- },
62
- ],
63
- };
64
-
65
- export default config;
66
- ```
67
-
68
- ### Running Your First Build
69
-
70
- Execute the bundler using one of these methods:
71
-
72
- #### CLI Execution
73
-
74
- with `npx` :
75
-
76
- ```bash
56
+ ```sh
77
57
  npx susee
78
58
  ```
79
59
 
80
- via `package.json` :
60
+ Or in `package.json`:
81
61
 
82
62
  ```json
83
63
  {
@@ -87,131 +67,118 @@ via `package.json` :
87
67
  }
88
68
  ```
89
69
 
90
- ```bash
91
- npm run build
92
- ```
70
+ ## Config Reference
93
71
 
94
- for global :
72
+ `SuSeeConfig`
95
73
 
96
- ```bash
97
- susee
74
+ ```ts
75
+ interface SuSeeConfig {
76
+ entryPoints: EntryPoint[];
77
+ outDir?: string; // default: "dist"
78
+ plugins?: (SuseePlugin | SuseePluginFunction)[]; // default: []
79
+ allowUpdatePackageJson?: boolean; // default: false
80
+ }
98
81
  ```
99
82
 
100
- #### Programmatic Execution
83
+ `EntryPoint`
101
84
 
102
85
  ```ts
103
- import { susee } from "susee";
104
-
105
- await susee();
86
+ type OutputFormat = ("commonjs" | "esm")[];
87
+
88
+ interface EntryPoint {
89
+ entry: string;
90
+ exportPath: "." | `./${string}`;
91
+ format?: OutputFormat; // default: ["esm"]
92
+ tsconfigFilePath?: string;
93
+ renameDuplicates?: boolean; // default: true
94
+ binary?: { name: string };
95
+ }
106
96
  ```
107
97
 
108
- The `susee()` function is an asynchronous operation that:
109
-
110
- 1. Loads configuration from `susee.config.ts`
111
- 2. Resolves the dependency tree
112
- 3. Bundles local dependencies
113
- 4. Compiles to target formats
114
- 5. Optionally updates `package.json`
115
-
116
- ## Configuration summary
117
-
118
- ### Top-Level Configuration Fields
98
+ ### Entry Validation Rules
119
99
 
120
- #### entryPoints
100
+ - At least one `entryPoint` is required.
101
+ - Duplicate `exportPath` values are rejected.
102
+ - Each `entry` file must exist.
121
103
 
122
- An array of [EntryPoint](#entrypoint-structure) objects defining the files to bundle. Each entry point represents a separate bundling operation with its own entry file, export path, and output configuration. The array must contain at least one entry point.
104
+ ### TypeScript Options Behavior
123
105
 
124
- #### plugins (optional)
106
+ For each entry point, Susee builds compiler options from:
125
107
 
126
- An optional array of plugin instances that provide transformation hooks. Plugins can be objects or factory functions and execute at different stages of the bundling pipeline (dependency, pre-process, post-process). Defaults to `[]` if not specified.
108
+ 1. `tsconfigFilePath` (if provided)
109
+ 2. project `tsconfig.json`
110
+ 3. Susee defaults
127
111
 
128
- #### allowUpdatePackageJson (optional)
112
+ Susee enforces/adjusts key options internally:
129
113
 
130
- Controls whether `susee` automatically updates the `package.json` file with generated `exports`, `main` fields, and `module` fields. When `true`, susee modifies the `package.json` to reflect the bundled outputs. When `false`, `package.json` remains unchanged.
131
- Defaults to `true` if not specified.
114
+ - `moduleResolution: "NodeNext"`
115
+ - `allowJs: true`
116
+ - `outDir` set per entry output path
117
+ - `types` includes `node`
118
+ - `lib` includes `ESNext`
132
119
 
133
- #### outDir (optional)
120
+ ## Plugin Hooks
134
121
 
135
- Specifies the base output directory where compiled files are written. This can be overridden per entry point if needed (though the current implementation uses a global outDir). Defaults to `"dist"` if not specified.
122
+ Susee supports these plugin stages:
136
123
 
137
- ### EntryPoint Structure
124
+ - `dependency`
125
+ - receives resolved dependency files and compiler options
126
+ - transforms dependency metadata/content before bundling
127
+ - `pre-process`
128
+ - receives bundled code before compilation
129
+ - `post-process`
130
+ - receives emitted JS output content per file
138
131
 
139
- Each entry point in the [entryPoints](#entrypoints) array defines a separate bundling target. The EntryPoint interface specifies the following fields:
132
+ Both sync and async plugins are supported.
140
133
 
141
- **entry**: The file path to the TypeScript entry file. This path is validated during configuration loading to ensure the file exists
134
+ ## Output Behavior
142
135
 
143
- **exportPath**: The package export path where this bundle will be exposed. Use "." for the main package export, or subpath like "./config" for additional exports. Duplicate export paths across entry points are not allowed and will cause validation failure.
136
+ - `format: ["esm"]`
137
+ - emits `.mjs` and `.d.mts`
138
+ - `format: ["commonjs"]`
139
+ - emits `.cjs` and `.d.cts`
140
+ - `format: ["esm", "commonjs"]`
141
+ - emits both sets
144
142
 
145
- **format (optional)**: Determines which module format(s), `commonjs`,`esm` or both `esm`and `commonjs` to generate.
146
- Defaults to `esm` if not specified.
143
+ When `allowUpdatePackageJson` is `true`, Susee can update:
147
144
 
148
- **tsconfigFilePath (optional)**: Optional path to a custom TypeScript configuration file for this specific entry point. If not specified, susee will resolve for TypesScript compiler options as follow :
145
+ - `type` (forced to `module`)
146
+ - `main`
147
+ - `module`
148
+ - `types`
149
+ - `exports` (including subpath exports from `exportPath`)
149
150
 
150
- Priority -
151
+ ## CLI
151
152
 
152
- 1. this custom `tsconfig.json`
153
-
154
- 2. `tsconfig.json` at root directory
155
-
156
- 3. default compiler options of `susee`
157
-
158
- Notes: You can control TypesScript compiler options from `tsconfig.json` except , `rootDir` , `outDir`,`module`.
159
-
160
- **renameDuplicates (optional)**: Controls whether susee automatically renames duplicate identifiers during the bundling process to avoid naming conflicts when merging files.(default to `true`).If you want to rename your self set to `false`, process will exit with code-1 and print where duplicate found.
161
-
162
- ## Plugins
163
-
164
- Plugins in the ecosystem have three common types:
165
-
166
- - `dependency` — transform dependency list before bundling
167
- - `pre-process` — modify the joined bundle text before compilation
168
- - `post-process` — modify emitted output files
169
-
170
- Plugins may be provided as objects or factories (functions returning the plugin). They may be synchronous or async — the bundler handles both.
171
-
172
- ## package.json updates
173
-
174
- When `allowUpdatePackageJson` is enabled, susee will:
175
-
176
- - set `type` to `module` (to ensure ESM compatibility)
177
- - add/update `main`, `module`, `types` and `exports` for the main export when building the package root
178
- - merge subpath exports for non-root `exportPath`s without overwriting unrelated exports
179
-
180
- Output file name hints (produced by the compiler):
181
-
182
- - ESM JS -> `.mjs`
183
- - ESM types -> `.d.mts`
184
- - CJS JS -> `.cjs`
185
- - CJS types -> `.d.cts`
186
-
187
- ## Limitations & notes
188
-
189
- - The bundler only processes local TypeScript files and does not bundle `node_modules` packages.
190
- - The entry file should be an ESM-compatible TypeScript file.
191
- - Exports from the entry file are not removed — only dependency exports may be stripped during bundling.
153
+ ```bash
154
+ susee
155
+ susee init
156
+ ```
192
157
 
193
- ## Roadmap
158
+ Any other argument combination exits with an error.
194
159
 
195
- Current environment support:
160
+ ## Local Development
196
161
 
197
- - Node.js only.
162
+ Common project scripts:
198
163
 
199
- Planned work:
164
+ ```bash
165
+ npm run build
166
+ npm run test
167
+ npm run lint
168
+ npm run fmt
169
+ npm run hooks:install
170
+ ```
200
171
 
201
- - [ ] Add first-class support for Deno environments.
202
- - [ ] Add browser-oriented library build support.
203
- - [ ] Improve workflows for building React-related libraries.
172
+ Notes:
204
173
 
205
- ## Contributing and tests
174
+ - `npm run test` opens an interactive selector (`scripts/susee-tests.ts`).
175
+ - Git hooks are tracked in `.githooks` and installed via `npm run hooks:install`.
206
176
 
207
- See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution workflow, pull request checklist, and development guidelines.
177
+ ## Contributing
208
178
 
209
- - Build and run tests with the repo scripts (see `package.json`):
179
+ Contributions are welcome for bug fixes, features, documentation, and code quality improvements.
210
180
 
211
- ```bash
212
- npm run build
213
- npm test
214
- ```
181
+ See detail in [CONTRIBUTING.md][file-contribute]
215
182
 
216
183
  ## License
217
184
 
@@ -220,4 +187,5 @@ npm test
220
187
  <!-- markdownlint-disable MD053 -->
221
188
 
222
189
  [license]: LICENSE
190
+ [file-contribute]: CONTRIBUTING.md
223
191
  [ptm]: https://github.com/phothinmg
package/bin/index.mjs CHANGED
@@ -1,3 +1,18 @@
1
1
  #!/usr/bin/env node
2
- import {susee} from "../dist/index.mjs";
3
- await susee();
2
+ import process from "node:process";
3
+ import init from "../src/binary/init.mjs";
4
+ import { susee } from "../dist/index.mjs";
5
+
6
+ async function suseeBuild() {
7
+ const args = process.argv.slice(2);
8
+ if (args.length === 0) {
9
+ await susee();
10
+ } else if (args.length === 1 && args[0] === "init") {
11
+ await init();
12
+ } else {
13
+ console.error("Unknown CLI usage");
14
+ process.exit(1);
15
+ }
16
+ }
17
+
18
+ await suseeBuild();
package/bin/init.mjs ADDED
@@ -0,0 +1,136 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import process from "node:process";
4
+ import readline from "node:readline/promises";
5
+ import tcolor from "@suseejs/tcolor";
6
+
7
+ const tsFileText = String.raw`
8
+ import type { SuSeeConfig } from "./src/index.js";
9
+
10
+ const config: SuSeeConfig = {
11
+ // Array of entry point objects.
12
+ // ----------------------------
13
+ entryPoints: [
14
+ // You can add more entry points for different export paths.
15
+ // NOTE: duplicate export paths are not allowed.
16
+ // --------------------------------------------
17
+ {
18
+ // (required) Entry file path.
19
+ entry: "src/index.ts", // replace with your entry file
20
+ // (required) Export path for this entry.
21
+ exportPath: ".", // "." stands for the main export path and can be set to "./foo", "./bar", etc.
22
+ // (optional) Output module formats ["commonjs"] or ["esm", "commonjs"], default: ["esm"].
23
+ // Uncomment the following line to edit.
24
+ //format: ["esm"],
25
+ // (optional) Rename duplicate declarations, default: true.
26
+ // Uncomment the following line to edit.
27
+ //renameDuplicates: true,
28
+ // (optional) Custom tsconfig.json path, default: undefined.
29
+ // Uncomment the following line to edit.
30
+ //tsconfigFilePath: undefined,
31
+ },
32
+ ],
33
+ // NOTE: the following options apply to all entry points.
34
+ // ----------------------------------------------------------
35
+ // (optional) Output directory, default: dist.
36
+ // Uncomment the following line to edit.
37
+ //outDir: "dist",
38
+ // (optional) Array of susee plugins, default: [].
39
+ // Uncomment the following line to edit.
40
+ //plugins: [],
41
+ // (optional) Allow susee to update your package.json, default: false.
42
+ // Uncomment the following line to edit.
43
+ //allowUpdatePackageJson: false,
44
+ };
45
+
46
+ export default config;
47
+ `.trim();
48
+
49
+ const jsFileText = String.raw`
50
+ /**
51
+ * @type {import("susee").SuSeeConfig}
52
+ */
53
+ const config = {
54
+ // Array of entry point objects.
55
+ // ----------------------------
56
+ entryPoints: [
57
+ // You can add more entry points for different export paths.
58
+ // NOTE: duplicate export paths are not allowed.
59
+ // --------------------------------------------
60
+ {
61
+ // (required) Entry file path.
62
+ entry: "src/index.ts", // replace with your entry file
63
+ // (required) Export path for this entry.
64
+ exportPath: ".", // "." stands for the main export path and can be set to "./foo", "./bar", etc.
65
+ // (optional) Output module formats ["commonjs"] or ["esm", "commonjs"], default: ["esm"].
66
+ // Uncomment the following line to edit.
67
+ //format: ["esm"],
68
+ // (optional) Rename duplicate declarations, default: true.
69
+ // Uncomment the following line to edit.
70
+ //renameDuplicates: true,
71
+ // (optional) Custom tsconfig.json path, default: undefined.
72
+ // Uncomment the following line to edit.
73
+ //tsconfigFilePath: undefined,
74
+ },
75
+ ],
76
+ // NOTE: the following options apply to all entry points.
77
+ // ----------------------------------------------------------
78
+ // (optional) Output directory, default: dist.
79
+ // Uncomment the following line to edit.
80
+ //outDir: "dist",
81
+ // (optional) Array of susee plugins, default: [].
82
+ // Uncomment the following line to edit.
83
+ //plugins: [],
84
+ // (optional) Allow susee to update your package.json, default: false.
85
+ // Uncomment the following line to edit.
86
+ //allowUpdatePackageJson: false,
87
+ };
88
+
89
+ export default config;
90
+ `.trim();
91
+ /**
92
+ *
93
+ * @returns {Promise<"commonjs"|"esm">}
94
+ */
95
+ async function getPackageType() {
96
+ const pkgFile = "package.json";
97
+ const pkgPath = path.resolve(process.cwd(), pkgFile);
98
+ const _pkg = await fs.promises.readFile(pkgPath, "utf8");
99
+ const pkg = JSON.parse(_pkg);
100
+ let type = "commonjs";
101
+ if (pkg.type && pkg.type === "module") type = "esm";
102
+ return type;
103
+ }
104
+
105
+ export default async function init() {
106
+ const rl = readline.createInterface({
107
+ input: process.stdin,
108
+ output: process.stdout,
109
+ });
110
+ const is_ts = await rl.question(tcolor.cyan("Is TypeScript project(y/n) : "));
111
+ const isTs = is_ts === "y" || is_ts === "Y" || is_ts === "" ? true : false;
112
+ rl.close();
113
+ let configFile;
114
+ let str;
115
+ if (isTs) {
116
+ configFile = "susee.config.ts";
117
+ str = tsFileText;
118
+ } else {
119
+ str = jsFileText;
120
+ const pkgType = await getPackageType();
121
+ switch (pkgType) {
122
+ case "commonjs":
123
+ configFile = "susee.config.mjs";
124
+ break;
125
+ case "esm":
126
+ configFile = "susee.config.js";
127
+ break;
128
+ }
129
+ }
130
+ const configFilePath = path.resolve(process.cwd(), configFile);
131
+ if (fs.existsSync(configFilePath)) await fs.promises.unlink(configFilePath);
132
+ await fs.promises.writeFile(configFilePath, str);
133
+ console.info(
134
+ tcolor.cyan(`Susee config file ${configFile} is created at project root`),
135
+ );
136
+ }