tsnv 0.0.0-dev.20260119180902
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/LICENSE +21 -0
- package/README.md +162 -0
- package/bin.js +3 -0
- package/dist/config.cjs +8 -0
- package/dist/config.d.cts +97 -0
- package/dist/config.d.ts +97 -0
- package/dist/config.js +7 -0
- package/dist/index.js +301 -0
- package/package.json +90 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Geunhyeok LEE
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# tsnv
|
|
4
|
+
|
|
5
|
+
Modern bundler for React Native libraries - fast, platform-aware, zero-config<br>
|
|
6
|
+
(powered by [Rolldown](https://rolldown.rs))
|
|
7
|
+
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
> [!NOTE]
|
|
11
|
+
> This project is under development
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- **Fast** - Powered by [Rolldown](https://rolldown.rs), a Rust-based bundler
|
|
16
|
+
- **Platform-aware** - Automatic handling of platform-specific modules (`.android.ts`, `.ios.ts`, `.native.ts`)
|
|
17
|
+
- **Dual format** - Supports both CommonJS and ESM output
|
|
18
|
+
- **TypeScript** - First-class TypeScript support with automatic `.d.ts` generation
|
|
19
|
+
- **Zero-config** - Sensible defaults that just work
|
|
20
|
+
- **Yarn PnP** - Works seamlessly with Yarn Plug'n'Play
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# npm
|
|
26
|
+
npm i -D tsnv
|
|
27
|
+
|
|
28
|
+
# pnpm
|
|
29
|
+
pnpm add -D tsnv
|
|
30
|
+
|
|
31
|
+
# yarn
|
|
32
|
+
yarn add -D tsnv
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
Just run:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npx tsnv
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
That's it. tsnv works out of the box with sensible defaults:
|
|
44
|
+
|
|
45
|
+
- Source directory: `src`
|
|
46
|
+
- Output directory: `dist`
|
|
47
|
+
- Format: ESM
|
|
48
|
+
- TypeScript declarations: enabled
|
|
49
|
+
|
|
50
|
+
### Custom Configuration (Optional)
|
|
51
|
+
|
|
52
|
+
If you need to customize the build, create a `tsnv.config.ts`:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { defineConfig } from 'tsnv';
|
|
56
|
+
|
|
57
|
+
export default defineConfig({
|
|
58
|
+
format: ['esm', 'cjs'],
|
|
59
|
+
sourcemap: true,
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Configuration
|
|
64
|
+
|
|
65
|
+
All configuration options with their default values:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { defineConfig } from 'tsnv';
|
|
69
|
+
|
|
70
|
+
export default defineConfig({
|
|
71
|
+
// Source directory
|
|
72
|
+
source: 'src',
|
|
73
|
+
|
|
74
|
+
// Output directory
|
|
75
|
+
outDir: 'dist',
|
|
76
|
+
|
|
77
|
+
// Output format: 'esm', 'cjs', or ['esm', 'cjs']
|
|
78
|
+
format: 'esm',
|
|
79
|
+
|
|
80
|
+
// Generate TypeScript declaration files
|
|
81
|
+
dts: true,
|
|
82
|
+
|
|
83
|
+
// Platform specifiers for module resolution
|
|
84
|
+
specifiers: ['android', 'ios', 'native'],
|
|
85
|
+
|
|
86
|
+
// Source file extensions
|
|
87
|
+
sourceExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
|
|
88
|
+
|
|
89
|
+
// Asset file extensions (Metro defaults)
|
|
90
|
+
assetExtensions: ['bmp', 'gif', 'jpg', 'jpeg', 'png', 'psd', 'svg', 'webp' /* ... */],
|
|
91
|
+
|
|
92
|
+
// Files to exclude from the build
|
|
93
|
+
exclude: /__(?:tests?|fixtures?|mocks?)__/,
|
|
94
|
+
|
|
95
|
+
// Generate source maps
|
|
96
|
+
sourcemap: false,
|
|
97
|
+
|
|
98
|
+
// Clean output directory before build
|
|
99
|
+
clean: true,
|
|
100
|
+
|
|
101
|
+
// Code injection options
|
|
102
|
+
banner: undefined,
|
|
103
|
+
footer: undefined,
|
|
104
|
+
intro: undefined,
|
|
105
|
+
outro: undefined,
|
|
106
|
+
|
|
107
|
+
// Experimental options
|
|
108
|
+
experimental: {
|
|
109
|
+
tsgo: false, // Use tsgo compiler
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Output Structure
|
|
115
|
+
|
|
116
|
+
### ESM only (`format: 'esm'`)
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
dist/
|
|
120
|
+
├── index.js
|
|
121
|
+
├── greeting.android.js
|
|
122
|
+
├── greeting.ios.js
|
|
123
|
+
└── types/
|
|
124
|
+
├── index.d.ts
|
|
125
|
+
├── greeting.android.d.ts
|
|
126
|
+
└── greeting.ios.d.ts
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### CommonJS only (`format: 'cjs'`)
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
dist/
|
|
133
|
+
├── index.js
|
|
134
|
+
├── greeting.android.js
|
|
135
|
+
├── greeting.ios.js
|
|
136
|
+
└── types/
|
|
137
|
+
├── index.d.ts
|
|
138
|
+
├── greeting.android.d.ts
|
|
139
|
+
└── greeting.ios.d.ts
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Dual format (`format: ['esm', 'cjs']`)
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
dist/
|
|
146
|
+
├── esm/
|
|
147
|
+
│ ├── index.js
|
|
148
|
+
│ ├── greeting.android.js
|
|
149
|
+
│ └── greeting.ios.js
|
|
150
|
+
├── cjs/
|
|
151
|
+
│ ├── index.js
|
|
152
|
+
│ ├── greeting.android.js
|
|
153
|
+
│ └── greeting.ios.js
|
|
154
|
+
└── types/
|
|
155
|
+
├── index.d.ts
|
|
156
|
+
├── greeting.android.d.ts
|
|
157
|
+
└── greeting.ios.d.ts
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## License
|
|
161
|
+
|
|
162
|
+
[MIT](./LICENSE)
|
package/bin.js
ADDED
package/dist/config.cjs
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { OutputOptions } from "rolldown";
|
|
2
|
+
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
type Format = 'esm' | 'cjs';
|
|
5
|
+
//#endregion
|
|
6
|
+
//#region src/config/types.d.ts
|
|
7
|
+
interface Config {
|
|
8
|
+
/**
|
|
9
|
+
* Defaults to `'src'`
|
|
10
|
+
*/
|
|
11
|
+
source?: string;
|
|
12
|
+
/**
|
|
13
|
+
* The directory where output files will be written.
|
|
14
|
+
*
|
|
15
|
+
* Defaults to `'dist'`
|
|
16
|
+
*/
|
|
17
|
+
outDir?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Files to exclude from the build.
|
|
20
|
+
*
|
|
21
|
+
* Defaults to `/__(?:tests?|fixtures?|mocks?)__/`
|
|
22
|
+
*/
|
|
23
|
+
exclude?: RegExp;
|
|
24
|
+
/**
|
|
25
|
+
* Expected format of generated code.
|
|
26
|
+
*
|
|
27
|
+
* Defaults to `'esm'`
|
|
28
|
+
*/
|
|
29
|
+
format?: Format | Format[];
|
|
30
|
+
/**
|
|
31
|
+
* Specifiers to resolve platform specific modules.
|
|
32
|
+
*
|
|
33
|
+
* Defaults to `['android', 'ios', 'native']`
|
|
34
|
+
*/
|
|
35
|
+
specifiers?: string[];
|
|
36
|
+
/**
|
|
37
|
+
* Source files extensions.
|
|
38
|
+
*
|
|
39
|
+
* Defaults to `['ts', 'tsx', 'js', 'jsx', 'json']`
|
|
40
|
+
*/
|
|
41
|
+
sourceExtensions?: string[];
|
|
42
|
+
/**
|
|
43
|
+
* Asset files extensions.
|
|
44
|
+
*
|
|
45
|
+
* Default to following extensions: [Metro's default asset extensions](https://github.com/facebook/metro/blob/v0.83.3/packages/metro-config/src/defaults/defaults.js)
|
|
46
|
+
*/
|
|
47
|
+
assetExtensions?: string[];
|
|
48
|
+
/**
|
|
49
|
+
* Enables generation of TypeScript declaration files (.d.ts).
|
|
50
|
+
*
|
|
51
|
+
* Defaults to `true`
|
|
52
|
+
*/
|
|
53
|
+
dts?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Generate source map files.
|
|
56
|
+
*/
|
|
57
|
+
sourcemap?: OutputOptions['sourcemap'];
|
|
58
|
+
/**
|
|
59
|
+
* Code to prepend to the beginning of each output chunk.
|
|
60
|
+
*/
|
|
61
|
+
banner?: OutputOptions['banner'];
|
|
62
|
+
/**
|
|
63
|
+
* Code to append to the end of each output chunk.
|
|
64
|
+
*/
|
|
65
|
+
footer?: OutputOptions['footer'];
|
|
66
|
+
/**
|
|
67
|
+
* Code to prepend inside the wrapper function (after banner, before actual code).
|
|
68
|
+
*/
|
|
69
|
+
intro?: OutputOptions['intro'];
|
|
70
|
+
/**
|
|
71
|
+
* Code to append inside the wrapper function (after actual code, before footer).
|
|
72
|
+
*/
|
|
73
|
+
outro?: OutputOptions['outro'];
|
|
74
|
+
/**
|
|
75
|
+
* Clean output directory before build.
|
|
76
|
+
*
|
|
77
|
+
* Defaults to `true`
|
|
78
|
+
*/
|
|
79
|
+
clean?: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Experimental configuration.
|
|
82
|
+
*/
|
|
83
|
+
experimental?: ExperimentalConfig;
|
|
84
|
+
}
|
|
85
|
+
interface ExperimentalConfig {
|
|
86
|
+
/**
|
|
87
|
+
* Whether to use the tsgo compiler.
|
|
88
|
+
*
|
|
89
|
+
* To use this option, make sure `@typescript/native-preview` is installed as a dependency.
|
|
90
|
+
*/
|
|
91
|
+
tsgo?: boolean;
|
|
92
|
+
}
|
|
93
|
+
//#endregion
|
|
94
|
+
//#region src/config.d.ts
|
|
95
|
+
declare function defineConfig(config: Config): Config;
|
|
96
|
+
//#endregion
|
|
97
|
+
export { defineConfig };
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { OutputOptions } from "rolldown";
|
|
2
|
+
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
type Format = 'esm' | 'cjs';
|
|
5
|
+
//#endregion
|
|
6
|
+
//#region src/config/types.d.ts
|
|
7
|
+
interface Config {
|
|
8
|
+
/**
|
|
9
|
+
* Defaults to `'src'`
|
|
10
|
+
*/
|
|
11
|
+
source?: string;
|
|
12
|
+
/**
|
|
13
|
+
* The directory where output files will be written.
|
|
14
|
+
*
|
|
15
|
+
* Defaults to `'dist'`
|
|
16
|
+
*/
|
|
17
|
+
outDir?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Files to exclude from the build.
|
|
20
|
+
*
|
|
21
|
+
* Defaults to `/__(?:tests?|fixtures?|mocks?)__/`
|
|
22
|
+
*/
|
|
23
|
+
exclude?: RegExp;
|
|
24
|
+
/**
|
|
25
|
+
* Expected format of generated code.
|
|
26
|
+
*
|
|
27
|
+
* Defaults to `'esm'`
|
|
28
|
+
*/
|
|
29
|
+
format?: Format | Format[];
|
|
30
|
+
/**
|
|
31
|
+
* Specifiers to resolve platform specific modules.
|
|
32
|
+
*
|
|
33
|
+
* Defaults to `['android', 'ios', 'native']`
|
|
34
|
+
*/
|
|
35
|
+
specifiers?: string[];
|
|
36
|
+
/**
|
|
37
|
+
* Source files extensions.
|
|
38
|
+
*
|
|
39
|
+
* Defaults to `['ts', 'tsx', 'js', 'jsx', 'json']`
|
|
40
|
+
*/
|
|
41
|
+
sourceExtensions?: string[];
|
|
42
|
+
/**
|
|
43
|
+
* Asset files extensions.
|
|
44
|
+
*
|
|
45
|
+
* Default to following extensions: [Metro's default asset extensions](https://github.com/facebook/metro/blob/v0.83.3/packages/metro-config/src/defaults/defaults.js)
|
|
46
|
+
*/
|
|
47
|
+
assetExtensions?: string[];
|
|
48
|
+
/**
|
|
49
|
+
* Enables generation of TypeScript declaration files (.d.ts).
|
|
50
|
+
*
|
|
51
|
+
* Defaults to `true`
|
|
52
|
+
*/
|
|
53
|
+
dts?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Generate source map files.
|
|
56
|
+
*/
|
|
57
|
+
sourcemap?: OutputOptions['sourcemap'];
|
|
58
|
+
/**
|
|
59
|
+
* Code to prepend to the beginning of each output chunk.
|
|
60
|
+
*/
|
|
61
|
+
banner?: OutputOptions['banner'];
|
|
62
|
+
/**
|
|
63
|
+
* Code to append to the end of each output chunk.
|
|
64
|
+
*/
|
|
65
|
+
footer?: OutputOptions['footer'];
|
|
66
|
+
/**
|
|
67
|
+
* Code to prepend inside the wrapper function (after banner, before actual code).
|
|
68
|
+
*/
|
|
69
|
+
intro?: OutputOptions['intro'];
|
|
70
|
+
/**
|
|
71
|
+
* Code to append inside the wrapper function (after actual code, before footer).
|
|
72
|
+
*/
|
|
73
|
+
outro?: OutputOptions['outro'];
|
|
74
|
+
/**
|
|
75
|
+
* Clean output directory before build.
|
|
76
|
+
*
|
|
77
|
+
* Defaults to `true`
|
|
78
|
+
*/
|
|
79
|
+
clean?: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Experimental configuration.
|
|
82
|
+
*/
|
|
83
|
+
experimental?: ExperimentalConfig;
|
|
84
|
+
}
|
|
85
|
+
interface ExperimentalConfig {
|
|
86
|
+
/**
|
|
87
|
+
* Whether to use the tsgo compiler.
|
|
88
|
+
*
|
|
89
|
+
* To use this option, make sure `@typescript/native-preview` is installed as a dependency.
|
|
90
|
+
*/
|
|
91
|
+
tsgo?: boolean;
|
|
92
|
+
}
|
|
93
|
+
//#endregion
|
|
94
|
+
//#region src/config.d.ts
|
|
95
|
+
declare function defineConfig(config: Config): Config;
|
|
96
|
+
//#endregion
|
|
97
|
+
export { defineConfig };
|
package/dist/config.js
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import { loadConfig } from "c12";
|
|
2
|
+
import { createDebug } from "obug";
|
|
3
|
+
import fs, { globSync } from "node:fs";
|
|
4
|
+
import * as pkg from "empathic/package";
|
|
5
|
+
import { build } from "rolldown";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { dts } from "rolldown-plugin-dts";
|
|
8
|
+
|
|
9
|
+
//#region src/common.ts
|
|
10
|
+
const debug$3 = createDebug("tsnv:common");
|
|
11
|
+
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/config/default.ts
|
|
14
|
+
const DEFAULT_CONFIG = {
|
|
15
|
+
source: "src",
|
|
16
|
+
outDir: "dist",
|
|
17
|
+
exclude: /__(?:tests?|fixtures?|mocks?)__/,
|
|
18
|
+
format: "esm",
|
|
19
|
+
specifiers: [
|
|
20
|
+
"android",
|
|
21
|
+
"ios",
|
|
22
|
+
"native"
|
|
23
|
+
],
|
|
24
|
+
sourceExtensions: [
|
|
25
|
+
"ts",
|
|
26
|
+
"tsx",
|
|
27
|
+
"js",
|
|
28
|
+
"jsx",
|
|
29
|
+
"json"
|
|
30
|
+
],
|
|
31
|
+
assetExtensions: [
|
|
32
|
+
"bmp",
|
|
33
|
+
"gif",
|
|
34
|
+
"jpg",
|
|
35
|
+
"jpeg",
|
|
36
|
+
"png",
|
|
37
|
+
"psd",
|
|
38
|
+
"svg",
|
|
39
|
+
"webp",
|
|
40
|
+
"xml",
|
|
41
|
+
"m4v",
|
|
42
|
+
"mov",
|
|
43
|
+
"mp4",
|
|
44
|
+
"mpeg",
|
|
45
|
+
"mpg",
|
|
46
|
+
"webm",
|
|
47
|
+
"aac",
|
|
48
|
+
"aiff",
|
|
49
|
+
"caf",
|
|
50
|
+
"m4a",
|
|
51
|
+
"mp3",
|
|
52
|
+
"wav",
|
|
53
|
+
"html",
|
|
54
|
+
"pdf",
|
|
55
|
+
"yaml",
|
|
56
|
+
"yml",
|
|
57
|
+
"otf",
|
|
58
|
+
"ttf",
|
|
59
|
+
"zip"
|
|
60
|
+
],
|
|
61
|
+
dts: true,
|
|
62
|
+
clean: true
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region ../../../.yarn/berry/cache/es-toolkit-npm-1.43.0-b8f13c51d9-10c0.zip/node_modules/es-toolkit/dist/util/invariant.mjs
|
|
67
|
+
function invariant(condition, message) {
|
|
68
|
+
if (condition) return;
|
|
69
|
+
if (typeof message === "string") throw new Error(message);
|
|
70
|
+
throw message;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region src/context.ts
|
|
75
|
+
async function resolveContext(cwd) {
|
|
76
|
+
const packageJsonPath = pkg.up({ cwd });
|
|
77
|
+
invariant(packageJsonPath, "could not find package.json");
|
|
78
|
+
const rawPackageJson = await fs.promises.readFile(packageJsonPath, "utf-8");
|
|
79
|
+
const packageJson = JSON.parse(rawPackageJson);
|
|
80
|
+
return {
|
|
81
|
+
cwd,
|
|
82
|
+
packageJson,
|
|
83
|
+
packageType: resolvePackageType(packageJson)
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function resolvePackageType(packageJson) {
|
|
87
|
+
switch (packageJson.type) {
|
|
88
|
+
case "module": return "esm";
|
|
89
|
+
default: return "cjs";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
//#endregion
|
|
94
|
+
//#region src/utils/path.ts
|
|
95
|
+
async function hasPlatformSpecificModule(id, importer, config$1) {
|
|
96
|
+
const specifiers = config$1.specifiers;
|
|
97
|
+
const resolveDir = path.dirname(importer);
|
|
98
|
+
let fileList = hasPlatformSpecificModule.cache.get(resolveDir);
|
|
99
|
+
if (fileList == null) {
|
|
100
|
+
fileList = (await fs.promises.readdir(resolveDir, {
|
|
101
|
+
recursive: false,
|
|
102
|
+
withFileTypes: true
|
|
103
|
+
})).filter((file) => file.isFile()).map(({ name }) => basenameWithoutExtension(name));
|
|
104
|
+
hasPlatformSpecificModule.cache.set(resolveDir, fileList);
|
|
105
|
+
}
|
|
106
|
+
return specifiers.some((specifier) => fileList.includes(`${basenameWithoutExtension(id)}.${specifier}`));
|
|
107
|
+
}
|
|
108
|
+
function basenameWithoutExtension(id) {
|
|
109
|
+
return path.basename(id, path.extname(id));
|
|
110
|
+
}
|
|
111
|
+
hasPlatformSpecificModule.cache = /* @__PURE__ */ new Map();
|
|
112
|
+
/**
|
|
113
|
+
* For React Native modules, the standard module specification cannot be followed.
|
|
114
|
+
*
|
|
115
|
+
* In the case of platform-specific modules, a prefix such as `android.js` or `ios.js` is added before the module name.
|
|
116
|
+
* If the standard module specification, which requires the full file path to be specified, is followed, platform-specific modules cannot be found.
|
|
117
|
+
*
|
|
118
|
+
* Therefore, the `.js` extension is used regardless of whether the module is ESM or CJS.
|
|
119
|
+
*/
|
|
120
|
+
function resolveFilename() {
|
|
121
|
+
return `[name].js`;
|
|
122
|
+
}
|
|
123
|
+
function isDts(filename) {
|
|
124
|
+
return filename.endsWith(".d.ts");
|
|
125
|
+
}
|
|
126
|
+
function removePlatformSpecificExtension(filename, extensions, specifiers) {
|
|
127
|
+
const extPattern = extensions.map((extension) => extension.replace(".", "")).join("|");
|
|
128
|
+
const regex = /* @__PURE__ */ new RegExp(`\\.(${specifiers.join("|")})\\.(${extPattern})$`);
|
|
129
|
+
return filename.replace(regex, ".$2");
|
|
130
|
+
}
|
|
131
|
+
function getUniquePlatformSpecificFiles(files$1, extensions, specifiers) {
|
|
132
|
+
const extPattern = extensions.map((e) => e.replace(".", "")).join("|");
|
|
133
|
+
const regex = /* @__PURE__ */ new RegExp(`\\.(${specifiers.join("|")})\\.(${extPattern})$`);
|
|
134
|
+
const seen = /* @__PURE__ */ new Set();
|
|
135
|
+
const result = [];
|
|
136
|
+
for (const file of files$1) {
|
|
137
|
+
const baseName = file.replace(regex, ".$2");
|
|
138
|
+
if (seen.has(baseName)) continue;
|
|
139
|
+
seen.add(baseName);
|
|
140
|
+
result.push(file);
|
|
141
|
+
}
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
//#endregion
|
|
146
|
+
//#region src/rolldown/plugins/dts.ts
|
|
147
|
+
const debug$2 = createDebug("tsnv:dts");
|
|
148
|
+
function dts$1(config$1) {
|
|
149
|
+
if (!config$1.dts) return null;
|
|
150
|
+
const dtsExtension = ["d"];
|
|
151
|
+
return [dts({
|
|
152
|
+
emitDtsOnly: true,
|
|
153
|
+
tsgo: config$1.experimental?.tsgo
|
|
154
|
+
}), {
|
|
155
|
+
name: "tsnv:dts-renamer",
|
|
156
|
+
outputOptions(options) {
|
|
157
|
+
return {
|
|
158
|
+
...options,
|
|
159
|
+
entryFileNames(chunkInfo) {
|
|
160
|
+
if (chunkInfo.name.endsWith(".d")) {
|
|
161
|
+
const newChunkName = `${removePlatformSpecificExtension(chunkInfo.name, dtsExtension, config$1.specifiers)}.ts`;
|
|
162
|
+
debug$2(`renaming ${chunkInfo.name} to ${newChunkName}`);
|
|
163
|
+
return newChunkName;
|
|
164
|
+
} else return chunkInfo.name;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
//#endregion
|
|
172
|
+
//#region src/rolldown/plugins/external.ts
|
|
173
|
+
const PLUGIN_NAME = "tsnv:external";
|
|
174
|
+
const debug$1 = createDebug(PLUGIN_NAME);
|
|
175
|
+
function external(context$1) {
|
|
176
|
+
const productionDependencies = Array.from(new Set([...Object.keys(context$1.packageJson.dependencies ?? {}), ...Object.keys(context$1.packageJson.peerDependencies ?? {})]));
|
|
177
|
+
debug$1("production dependencies", productionDependencies);
|
|
178
|
+
return {
|
|
179
|
+
name: PLUGIN_NAME,
|
|
180
|
+
async resolveId(id, importer, extraOptions) {
|
|
181
|
+
if (extraOptions.isEntry || importer == null) return;
|
|
182
|
+
if (productionDependencies.some((packageName) => isPackageImportSource(packageName, id))) return {
|
|
183
|
+
id,
|
|
184
|
+
external: true
|
|
185
|
+
};
|
|
186
|
+
const extname = path.extname(id);
|
|
187
|
+
if (extname) {
|
|
188
|
+
if (context$1.config.assetExtensions.includes(extname)) return {
|
|
189
|
+
id,
|
|
190
|
+
external: true
|
|
191
|
+
};
|
|
192
|
+
if (!context$1.config.sourceExtensions.includes(extname)) throw new Error(`Unsupported file extension: ${extname}`);
|
|
193
|
+
}
|
|
194
|
+
const resolved = await this.resolve(id, importer, extraOptions);
|
|
195
|
+
if (resolved == null && await hasPlatformSpecificModule(id, importer, context$1.config)) return {
|
|
196
|
+
id,
|
|
197
|
+
external: true,
|
|
198
|
+
moduleSideEffects: true
|
|
199
|
+
};
|
|
200
|
+
return resolved;
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function isPackageImportSource(packageName, id) {
|
|
205
|
+
return id === packageName || id.startsWith(`${packageName}/`);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
//#endregion
|
|
209
|
+
//#region src/rolldown/build-options.ts
|
|
210
|
+
function resolveBuildOptions(context$1, options) {
|
|
211
|
+
const pluginContext = {
|
|
212
|
+
...context$1,
|
|
213
|
+
config: options.config
|
|
214
|
+
};
|
|
215
|
+
const baseOptions = {
|
|
216
|
+
input: options.files,
|
|
217
|
+
plugins: [external(pluginContext)],
|
|
218
|
+
output: {
|
|
219
|
+
banner: options.config.banner,
|
|
220
|
+
footer: options.config.footer,
|
|
221
|
+
intro: options.config.intro,
|
|
222
|
+
outro: options.config.outro,
|
|
223
|
+
sourcemap: options.config.sourcemap,
|
|
224
|
+
preserveModulesRoot: options.config.source,
|
|
225
|
+
preserveModules: true
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
let formats;
|
|
229
|
+
if (Array.isArray(options.config.format)) formats = options.config.format;
|
|
230
|
+
else formats = [options.config.format];
|
|
231
|
+
const uniqueFormats = Array.from(new Set(formats));
|
|
232
|
+
const isSingleFormat = uniqueFormats.length === 1;
|
|
233
|
+
const filename = resolveFilename();
|
|
234
|
+
const resolvedBuildOptions = uniqueFormats.map((format) => {
|
|
235
|
+
return {
|
|
236
|
+
...baseOptions,
|
|
237
|
+
output: {
|
|
238
|
+
...baseOptions.output,
|
|
239
|
+
dir: isSingleFormat ? options.config.outDir : path.join(options.config.outDir, format),
|
|
240
|
+
cleanDir: options.config.clean,
|
|
241
|
+
format,
|
|
242
|
+
entryFileNames: filename,
|
|
243
|
+
chunkFileNames: filename
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
});
|
|
247
|
+
if (options.config.dts) resolvedBuildOptions.push({
|
|
248
|
+
...baseOptions,
|
|
249
|
+
input: getUniquePlatformSpecificFiles(options.files, options.config.sourceExtensions, options.config.specifiers),
|
|
250
|
+
plugins: [...Array.isArray(baseOptions.plugins) ? baseOptions.plugins : [baseOptions.plugins], dts$1(options.config)],
|
|
251
|
+
output: {
|
|
252
|
+
...baseOptions.output,
|
|
253
|
+
cleanDir: options.config.clean,
|
|
254
|
+
dir: path.join(options.config.outDir, "types"),
|
|
255
|
+
format: "esm",
|
|
256
|
+
entryFileNames: filename,
|
|
257
|
+
chunkFileNames: filename
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
return resolvedBuildOptions;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
//#endregion
|
|
264
|
+
//#region src/rolldown/index.ts
|
|
265
|
+
const debug = createDebug("tsnv:build");
|
|
266
|
+
async function build$1(context$1, options) {
|
|
267
|
+
const buildOptions = resolveBuildOptions(context$1, options);
|
|
268
|
+
debug("Resolved build options", buildOptions);
|
|
269
|
+
for (const buildOption of buildOptions) await build(buildOption);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
//#endregion
|
|
273
|
+
//#region src/utils/fs.ts
|
|
274
|
+
function collectFiles(config$1) {
|
|
275
|
+
const files$1 = globSync(`**/*.{${config$1.sourceExtensions.join(",")}}`, {
|
|
276
|
+
cwd: config$1.source,
|
|
277
|
+
exclude: (filename) => config$1.exclude.test(filename) || isDts(filename)
|
|
278
|
+
});
|
|
279
|
+
if (files$1.length === 0) throw new Error(`No files found in ${path.resolve(config$1.source)}`);
|
|
280
|
+
return files$1.map((file) => path.join(config$1.source, file));
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
//#endregion
|
|
284
|
+
//#region src/index.ts
|
|
285
|
+
debug$3("Loading config...");
|
|
286
|
+
const { config } = await loadConfig({
|
|
287
|
+
configFile: "tsnv.config",
|
|
288
|
+
defaultConfig: DEFAULT_CONFIG
|
|
289
|
+
});
|
|
290
|
+
debug$3("Config loaded", config);
|
|
291
|
+
const context = await resolveContext(process.cwd());
|
|
292
|
+
debug$3("Resolved context", context);
|
|
293
|
+
const files = await collectFiles(config);
|
|
294
|
+
debug$3("Collected files", files);
|
|
295
|
+
await build$1(context, {
|
|
296
|
+
files,
|
|
297
|
+
config
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
//#endregion
|
|
301
|
+
export { };
|
package/package.json
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tsnv",
|
|
3
|
+
"version": "0.0.0-dev.20260119180902",
|
|
4
|
+
"description": "Modern build toolkit for React Native libraries",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Geunhyeok Lee <dev.ghlee@gmail.com>",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/leegeunhyeok/tsnv.git",
|
|
10
|
+
"directory": "."
|
|
11
|
+
},
|
|
12
|
+
"bin": "bin.js",
|
|
13
|
+
"files": [
|
|
14
|
+
"bin.js",
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"type": "module",
|
|
18
|
+
"main": "./dist/config.cjs",
|
|
19
|
+
"module": "./dist/config.js",
|
|
20
|
+
"types": "./dist/config.d.cjs",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"import": {
|
|
24
|
+
"types": "./dist/config.d.js",
|
|
25
|
+
"default": "./dist/config.js"
|
|
26
|
+
},
|
|
27
|
+
"require": {
|
|
28
|
+
"types": "./dist/config.d.cjs",
|
|
29
|
+
"default": "./dist/config.cjs"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"./package.json": "./package.json"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"prepack": "yarn build",
|
|
36
|
+
"dev": "tsx src/index.ts",
|
|
37
|
+
"typecheck": "tsc --noEmit",
|
|
38
|
+
"lint": "oxlint --type-aware --deny-warnings",
|
|
39
|
+
"lint:fix": "oxlint --type-aware --deny-warnings --fix",
|
|
40
|
+
"fmt": "oxfmt",
|
|
41
|
+
"test": "vitest --run",
|
|
42
|
+
"test:e2e": "vitest --run --config vitest.e2e.config.ts",
|
|
43
|
+
"build": "tsdown",
|
|
44
|
+
"scripts:version": ".scripts/version.sh",
|
|
45
|
+
"scripts:publish": ".scripts/publish.sh"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"c12": "^3.3.3",
|
|
49
|
+
"empathic": "^2.0.0",
|
|
50
|
+
"obug": "^2.1.1",
|
|
51
|
+
"rolldown": "1.0.0-beta.60",
|
|
52
|
+
"rolldown-plugin-dts": "0.21.2",
|
|
53
|
+
"tsdown": "0.20.0-beta.3"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@changesets/cli": "^2.29.8",
|
|
57
|
+
"@types/node": "^24.10.1",
|
|
58
|
+
"es-toolkit": "^1.43.0",
|
|
59
|
+
"oxfmt": "^0.24.0",
|
|
60
|
+
"oxlint": "^1.39.0",
|
|
61
|
+
"oxlint-tsgolint": "^0.11.1",
|
|
62
|
+
"picocolors": "^1.1.1",
|
|
63
|
+
"tsx": "^4.21.0",
|
|
64
|
+
"typescript": "^5.9.3",
|
|
65
|
+
"vitest": "^4.0.17",
|
|
66
|
+
"zx": "^8.8.5"
|
|
67
|
+
},
|
|
68
|
+
"dependenciesMeta": {
|
|
69
|
+
"@typescript/native-preview": {},
|
|
70
|
+
"oxfmt": {
|
|
71
|
+
"unplugged": true
|
|
72
|
+
},
|
|
73
|
+
"oxlint": {
|
|
74
|
+
"unplugged": true
|
|
75
|
+
},
|
|
76
|
+
"oxlint-tsgolint": {
|
|
77
|
+
"unplugged": true
|
|
78
|
+
},
|
|
79
|
+
"typescript": {}
|
|
80
|
+
},
|
|
81
|
+
"peerDependencies": {
|
|
82
|
+
"@typescript/native-preview": "*",
|
|
83
|
+
"typescript": "*"
|
|
84
|
+
},
|
|
85
|
+
"engines": {
|
|
86
|
+
"node": ">=20.19.0"
|
|
87
|
+
},
|
|
88
|
+
"packageManager": "yarn@4.12.0",
|
|
89
|
+
"stableVersion": "0.0.0"
|
|
90
|
+
}
|