modular-library 0.0.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/.github/FUNDING.yml +12 -0
- package/.github/dependabot.yml +9 -0
- package/.github/workflows/ci.yml +32 -0
- package/AGENTS.md +58 -0
- package/README.md +128 -0
- package/biome.json +40 -0
- package/package.json +72 -0
- package/src/createEntries.ts +59 -0
- package/src/rolldown/index.ts +2 -0
- package/src/rolldown/plugin.test.ts +129 -0
- package/src/rolldown/plugin.ts +34 -0
- package/src/rollup/index.ts +2 -0
- package/src/rollup/plugin.test.ts +114 -0
- package/src/rollup/plugin.ts +34 -0
- package/src/vite/index.ts +2 -0
- package/src/vite/plugin.test.ts +192 -0
- package/src/vite/plugin.ts +40 -0
- package/test/fixture/input1.js +2 -0
- package/test/fixture/input2.js +2 -0
- package/tsconfig.json +19 -0
- package/vite.config.ts +36 -0
- package/vitest.config.ts +18 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# These are supported funding model platforms
|
|
2
|
+
|
|
3
|
+
github: [alfredosalzillo]
|
|
4
|
+
patreon: # Replace with a single Patreon username
|
|
5
|
+
open_collective: # Replace with a single Open Collective username
|
|
6
|
+
ko_fi: # Replace with a single Ko-fi username
|
|
7
|
+
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
|
8
|
+
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
|
9
|
+
liberapay: # Replace with a single Liberapay username
|
|
10
|
+
issuehunt: # Replace with a single IssueHunt username
|
|
11
|
+
otechie: # Replace with a single Otechie username
|
|
12
|
+
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
strategy:
|
|
11
|
+
fail-fast: false
|
|
12
|
+
matrix:
|
|
13
|
+
node-version: [22, 23, 24, 25]
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- name: Checkout repository
|
|
17
|
+
uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Setup Node.js
|
|
20
|
+
uses: actions/setup-node@v4
|
|
21
|
+
with:
|
|
22
|
+
node-version: ${{ matrix.node-version }}
|
|
23
|
+
cache: npm
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: npm ci
|
|
27
|
+
|
|
28
|
+
- name: Build
|
|
29
|
+
run: npm run build
|
|
30
|
+
|
|
31
|
+
- name: Test
|
|
32
|
+
run: npm test
|
package/AGENTS.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
This file provides context and instructions for AI coding agents working on this project.
|
|
4
|
+
|
|
5
|
+
## Setup Commands
|
|
6
|
+
|
|
7
|
+
- Install dependencies: `npm install`
|
|
8
|
+
- Build the project: `npm run build`
|
|
9
|
+
- Run tests: `npm test`
|
|
10
|
+
- Run lint checks: `npm run lint`
|
|
11
|
+
|
|
12
|
+
## Technology Stack
|
|
13
|
+
|
|
14
|
+
- **TypeScript**: Use TypeScript for all code changes. Follow the existing configuration in `tsconfig.json`.
|
|
15
|
+
- Keep this `AGENTS.md` technology stack section up to date whenever the project tech stack changes.
|
|
16
|
+
|
|
17
|
+
## Development Workflow
|
|
18
|
+
|
|
19
|
+
1. **Keep changes minimal**: Implement the smallest safe change that resolves the issue.
|
|
20
|
+
2. **Build**: Before submitting, ensure the project compiles by running `npm run build`.
|
|
21
|
+
3. **Validate**: Run relevant tests with `npm test` when behavior changes.
|
|
22
|
+
4. **Code Style**: Follow the existing style and naming patterns in the codebase.
|
|
23
|
+
5. **GitHub Workflows**: To test workflows locally, use [`act`](https://nektosact.com/).
|
|
24
|
+
|
|
25
|
+
## Temporary Files
|
|
26
|
+
|
|
27
|
+
- **Temporary Directory**: Always place temporary files in the `.ai-tmp` directory.
|
|
28
|
+
|
|
29
|
+
## Commit Message Guidelines
|
|
30
|
+
|
|
31
|
+
When creating commits, use **Conventional Commits**.
|
|
32
|
+
|
|
33
|
+
### Format
|
|
34
|
+
|
|
35
|
+
```text
|
|
36
|
+
<type>(<scope>): <description>
|
|
37
|
+
|
|
38
|
+
[optional body]
|
|
39
|
+
|
|
40
|
+
[optional footer(s)]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Types
|
|
44
|
+
|
|
45
|
+
- `feat`: A new feature (minor version update).
|
|
46
|
+
- `fix`: A bug fix (patch version update).
|
|
47
|
+
- `docs`: Documentation changes only.
|
|
48
|
+
- `style`: Changes that do not affect the meaning of the code (white-space, formatting, etc).
|
|
49
|
+
- `refactor`: A code change that neither fixes a bug nor adds a feature.
|
|
50
|
+
- `perf`: A code change that improves performance.
|
|
51
|
+
- `test`: Adding missing tests or correcting existing tests.
|
|
52
|
+
- `build`: Changes that affect the build system or external dependencies.
|
|
53
|
+
- `ci`: Changes to CI configuration files and scripts.
|
|
54
|
+
- `chore`: Other changes that don't modify src or test files.
|
|
55
|
+
|
|
56
|
+
### Breaking Changes
|
|
57
|
+
|
|
58
|
+
Breaking changes must be indicated by a `!` after the type/scope or by including `BREAKING CHANGE:` in the footer.
|
package/README.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# modular-library
|
|
2
|
+
|
|
3
|
+
`modular-library` is a utility library for generating modular libraries.
|
|
4
|
+
|
|
5
|
+
## What is a modular library?
|
|
6
|
+
|
|
7
|
+
A **modular library** is a library split into small, focused modules instead of one large, monolithic package.
|
|
8
|
+
|
|
9
|
+
This approach helps you:
|
|
10
|
+
|
|
11
|
+
- keep each part of the library easier to understand and maintain,
|
|
12
|
+
- reuse modules independently across projects,
|
|
13
|
+
- reduce coupling between features,
|
|
14
|
+
- improve scalability as the codebase grows,
|
|
15
|
+
- be tree-shakeable so consumers only bundle what they import,
|
|
16
|
+
- expose clear entry points for each public feature or component.
|
|
17
|
+
|
|
18
|
+
Typical examples of modular libraries include:
|
|
19
|
+
|
|
20
|
+
- UI libraries exposing per-component entries (for example `@acme/ui/button`, `@acme/ui/modal`),
|
|
21
|
+
- utility libraries exposing per-domain modules (for example `@acme/utils/date`, `@acme/utils/string`),
|
|
22
|
+
- SDKs exposing feature-based modules (for example `@acme/sdk/auth`, `@acme/sdk/storage`).
|
|
23
|
+
|
|
24
|
+
With `modular-library`, the goal is to make the creation of this kind of modular architecture faster and more consistent.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
> **Node.js requirement:** This library supports only Node.js `22` or newer.
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install modular-library
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
`modular-library` provides dedicated plugins for `vite`, `rollup`, and `rolldown`.
|
|
37
|
+
|
|
38
|
+
### Vite
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
// vite.config.ts
|
|
42
|
+
import { defineConfig } from "vite";
|
|
43
|
+
import modularLibrary from "modular-library/vite";
|
|
44
|
+
|
|
45
|
+
export default defineConfig({
|
|
46
|
+
plugins: [modularLibrary()],
|
|
47
|
+
build: {
|
|
48
|
+
lib: {
|
|
49
|
+
entry: ["src/**/*.ts"],
|
|
50
|
+
formats: ["es"],
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Rollup
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
// rollup.config.ts
|
|
60
|
+
import modularLibrary from "modular-library/rollup";
|
|
61
|
+
|
|
62
|
+
export default {
|
|
63
|
+
input: ["src/**/*.ts"],
|
|
64
|
+
output: {
|
|
65
|
+
dir: "dist",
|
|
66
|
+
format: "es",
|
|
67
|
+
},
|
|
68
|
+
plugins: [modularLibrary()],
|
|
69
|
+
};
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Rolldown
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
// rolldown.config.ts
|
|
76
|
+
import modularLibrary from "modular-library/rolldown";
|
|
77
|
+
|
|
78
|
+
export default {
|
|
79
|
+
input: ["src/**/*.ts"],
|
|
80
|
+
output: {
|
|
81
|
+
dir: "dist",
|
|
82
|
+
format: "es",
|
|
83
|
+
},
|
|
84
|
+
plugins: [modularLibrary()],
|
|
85
|
+
};
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Options
|
|
89
|
+
|
|
90
|
+
All plugin variants support the same options:
|
|
91
|
+
|
|
92
|
+
- `relative` (default: `"src/"`): base path removed from generated output keys.
|
|
93
|
+
- `glob`: [`fs.globSync`](https://nodejs.org/api/fs.html#fsglobsyncpattern-options) options.
|
|
94
|
+
- `transformOutputPath(outputPath, inputPath)`: customize each generated output path.
|
|
95
|
+
|
|
96
|
+
Example with options:
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
import modularLibrary from "modular-library/rollup";
|
|
100
|
+
|
|
101
|
+
export default {
|
|
102
|
+
input: ["src/**/*.ts"],
|
|
103
|
+
output: {
|
|
104
|
+
dir: "dist",
|
|
105
|
+
format: "es",
|
|
106
|
+
},
|
|
107
|
+
plugins: [
|
|
108
|
+
modularLibrary({
|
|
109
|
+
relative: "src/",
|
|
110
|
+
transformOutputPath: (outputPath) => `modules/${outputPath}`,
|
|
111
|
+
}),
|
|
112
|
+
],
|
|
113
|
+
};
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Development
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
npm install
|
|
120
|
+
npm run build
|
|
121
|
+
npm test
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
To test GitHub workflows locally, you can use [`act`](https://nektosact.com/).
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
|
|
128
|
+
ISC
|
package/biome.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/2.4.6/schema.json",
|
|
3
|
+
"vcs": {
|
|
4
|
+
"enabled": true,
|
|
5
|
+
"clientKind": "git",
|
|
6
|
+
"useIgnoreFile": true
|
|
7
|
+
},
|
|
8
|
+
"files": {
|
|
9
|
+
"ignoreUnknown": true,
|
|
10
|
+
"includes": ["**", "!node_modules", "!.next", "!dist", "!build"]
|
|
11
|
+
},
|
|
12
|
+
"formatter": {
|
|
13
|
+
"enabled": true,
|
|
14
|
+
"indentStyle": "space",
|
|
15
|
+
"indentWidth": 2
|
|
16
|
+
},
|
|
17
|
+
"javascript": {
|
|
18
|
+
"formatter": {
|
|
19
|
+
"quoteStyle": "double"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"linter": {
|
|
23
|
+
"enabled": true,
|
|
24
|
+
"rules": {
|
|
25
|
+
"recommended": true
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"assist": {
|
|
29
|
+
"actions": {
|
|
30
|
+
"source": {
|
|
31
|
+
"organizeImports": {
|
|
32
|
+
"level": "on",
|
|
33
|
+
"options": {
|
|
34
|
+
"groups": [":PACKAGE:", ":PATH:"]
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "modular-library",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"exports": {
|
|
6
|
+
"./rollup": {
|
|
7
|
+
"types": "./dist/rollup/index.d.ts",
|
|
8
|
+
"import": "./dist/rollup/index.es.js",
|
|
9
|
+
"require": "./dist/rollup/index.cjs.js"
|
|
10
|
+
},
|
|
11
|
+
"./rolldown": {
|
|
12
|
+
"types": "./dist/rolldown/index.d.ts",
|
|
13
|
+
"import": "./dist/rolldown/index.es.js",
|
|
14
|
+
"require": "./dist/rolldown/index.cjs.js"
|
|
15
|
+
},
|
|
16
|
+
"./vite": {
|
|
17
|
+
"types": "./dist/vite/index.d.ts",
|
|
18
|
+
"import": "./dist/vite/index.es.js",
|
|
19
|
+
"require": "./dist/vite/index.cjs.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "vite build",
|
|
24
|
+
"lint": "biome check .",
|
|
25
|
+
"format": "biome format --write .",
|
|
26
|
+
"test": "vitest run --coverage"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"modular-library",
|
|
30
|
+
"vite-plugin",
|
|
31
|
+
"rollup-plugin",
|
|
32
|
+
"rolldown-plugin",
|
|
33
|
+
"multi-entry",
|
|
34
|
+
"multi-input",
|
|
35
|
+
"tree-shaking",
|
|
36
|
+
"typescript",
|
|
37
|
+
"bundler"
|
|
38
|
+
],
|
|
39
|
+
"author": "alfredo salzillo <alfredosalzillo93@gmail.com>",
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "https://github.com/alfredosalzillo/modular-library.git"
|
|
44
|
+
},
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/alfredosalzillo/modular-library/issues"
|
|
47
|
+
},
|
|
48
|
+
"funding": {
|
|
49
|
+
"url": "https://github.com/sponsors/alfredosalzillo"
|
|
50
|
+
},
|
|
51
|
+
"homepage": "https://github.com/alfredosalzillo/modular-library#readme",
|
|
52
|
+
"description": "Plugins for Vite, Rollup, and Rolldown to build modular multi-entry TypeScript libraries.",
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@biomejs/biome": "^2.4.11",
|
|
55
|
+
"@rollup/plugin-json": "^6.1.0",
|
|
56
|
+
"@types/node": "^25.6.0",
|
|
57
|
+
"@vitejs/plugin-react": "^6.0.1",
|
|
58
|
+
"@vitest/coverage-v8": "^4.1.4",
|
|
59
|
+
"rolldown": "^1.0.0-rc.15",
|
|
60
|
+
"rollup": "^4.60.1",
|
|
61
|
+
"typescript": "^6.0.2",
|
|
62
|
+
"unplugin-dts": "^1.0.0-beta.6",
|
|
63
|
+
"vite": "^8.0.8",
|
|
64
|
+
"vitest": "^4.1.4"
|
|
65
|
+
},
|
|
66
|
+
"volta": {
|
|
67
|
+
"node": "25.9.0"
|
|
68
|
+
},
|
|
69
|
+
"engines": {
|
|
70
|
+
"node": ">=22.0.0"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { globSync, type GlobOptionsWithoutFileTypes } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
const isString = (value: unknown): value is string => typeof value === "string";
|
|
5
|
+
const outputFileName = (filePath: string) =>
|
|
6
|
+
filePath.replace(/\.[^/.]+$/, "").replace(/\\/g, "/");
|
|
7
|
+
|
|
8
|
+
export type CreateEntryInput = string | string[] | Record<string, string>;
|
|
9
|
+
type CreateEntriesGlobOptions = GlobOptionsWithoutFileTypes;
|
|
10
|
+
|
|
11
|
+
export type CreateEntriesOptions = {
|
|
12
|
+
glob?: CreateEntriesGlobOptions;
|
|
13
|
+
relative?: string;
|
|
14
|
+
transformOutputPath?: (path: string, fileName: string) => string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const defaultOptions = {
|
|
18
|
+
relative: `src/`,
|
|
19
|
+
};
|
|
20
|
+
const createEntries = (
|
|
21
|
+
input: CreateEntryInput,
|
|
22
|
+
options?: CreateEntriesOptions,
|
|
23
|
+
) => {
|
|
24
|
+
// flat to enable input to be a string or an array
|
|
25
|
+
const inputs = [input].flat();
|
|
26
|
+
// separate globs inputs string from others to enable input to be a mixed array too
|
|
27
|
+
const globs = inputs.filter((value) => isString(value));
|
|
28
|
+
const others = inputs.filter((value) => !isString(value));
|
|
29
|
+
const normalizedGlobs = globs.map((glob) => glob.replace(/\\/g, "/"));
|
|
30
|
+
|
|
31
|
+
// get files from the strings and return as entries Object
|
|
32
|
+
const entries = globSync(normalizedGlobs, {
|
|
33
|
+
...options?.glob,
|
|
34
|
+
withFileTypes: false,
|
|
35
|
+
}).map((name) => {
|
|
36
|
+
const filePath = path.relative(
|
|
37
|
+
options?.relative ?? defaultOptions.relative,
|
|
38
|
+
name,
|
|
39
|
+
);
|
|
40
|
+
const isRelative = !filePath.startsWith(`../`);
|
|
41
|
+
const relativeFilePath = isRelative ? filePath : path.relative(`./`, name);
|
|
42
|
+
return [
|
|
43
|
+
outputFileName(
|
|
44
|
+
options?.transformOutputPath
|
|
45
|
+
? options.transformOutputPath(relativeFilePath, name)
|
|
46
|
+
: relativeFilePath,
|
|
47
|
+
),
|
|
48
|
+
name,
|
|
49
|
+
];
|
|
50
|
+
});
|
|
51
|
+
return Object.assign(
|
|
52
|
+
{},
|
|
53
|
+
Object.fromEntries(entries),
|
|
54
|
+
// add no globs input to the result
|
|
55
|
+
...others,
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export default createEntries;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import type { InputOptions } from "rolldown";
|
|
2
|
+
import modularLibrary from "./plugin";
|
|
3
|
+
|
|
4
|
+
const expectedOutput = ["fixture/input1", "fixture/input2"].sort();
|
|
5
|
+
|
|
6
|
+
const applyOptions = (
|
|
7
|
+
options: InputOptions,
|
|
8
|
+
pluginOptions?: Parameters<typeof modularLibrary>[0],
|
|
9
|
+
) => {
|
|
10
|
+
const plugin = modularLibrary(pluginOptions);
|
|
11
|
+
if (!plugin.options) {
|
|
12
|
+
throw new Error("options hook is required");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const context = {
|
|
16
|
+
warn: vi.fn(),
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const result = plugin.options.call(context as never, options);
|
|
20
|
+
return {
|
|
21
|
+
result,
|
|
22
|
+
warn: context.warn,
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
describe("modular-library/rolldown", () => {
|
|
27
|
+
it("should have name modular-library/rolldown", () => {
|
|
28
|
+
const plugin = modularLibrary({ relative: "./test" });
|
|
29
|
+
expect(plugin.name).toBe("modular-library/rolldown");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("should resolve glob", () => {
|
|
33
|
+
const { result } = applyOptions(
|
|
34
|
+
{
|
|
35
|
+
input: ["test/fixture/**/*.js"],
|
|
36
|
+
},
|
|
37
|
+
{ relative: "./test" },
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
expect(Object.keys(result.input as Record<string, string>).sort()).toEqual(
|
|
41
|
+
expectedOutput,
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("should accept a simple string as input", () => {
|
|
46
|
+
const { result } = applyOptions(
|
|
47
|
+
{
|
|
48
|
+
input: "test/fixture/**/*.js",
|
|
49
|
+
},
|
|
50
|
+
{ relative: "./test" },
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
expect(Object.keys(result.input as Record<string, string>).sort()).toEqual(
|
|
54
|
+
expectedOutput,
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("should accept an array of strings as input", () => {
|
|
59
|
+
const { result } = applyOptions(
|
|
60
|
+
{
|
|
61
|
+
input: ["test/fixture/**/*.js"],
|
|
62
|
+
},
|
|
63
|
+
{ relative: "./test" },
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
expect(Object.keys(result.input as Record<string, string>).sort()).toEqual(
|
|
67
|
+
expectedOutput,
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("should remove unresolved glob", () => {
|
|
72
|
+
const { result } = applyOptions(
|
|
73
|
+
{
|
|
74
|
+
input: ["test/fixture/**/*.js", "/not-found/file.js"],
|
|
75
|
+
},
|
|
76
|
+
{ relative: "./test" },
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
expect(Object.keys(result.input as Record<string, string>).sort()).toEqual(
|
|
80
|
+
expectedOutput,
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("should resolve relative to src as default", () => {
|
|
85
|
+
const { result: outputFilesWithNoOptions } = applyOptions({
|
|
86
|
+
input: ["test/fixture/**/*.js"],
|
|
87
|
+
});
|
|
88
|
+
const { result: outputFilesWithNoRelativeOption } = applyOptions(
|
|
89
|
+
{
|
|
90
|
+
input: ["./test/fixture/**/*.js"],
|
|
91
|
+
},
|
|
92
|
+
{},
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
expect(
|
|
96
|
+
Object.keys(
|
|
97
|
+
outputFilesWithNoOptions.input as Record<string, string>,
|
|
98
|
+
).sort(),
|
|
99
|
+
).toEqual(["test/fixture/input1", "test/fixture/input2"]);
|
|
100
|
+
expect(
|
|
101
|
+
Object.keys(
|
|
102
|
+
outputFilesWithNoRelativeOption.input as Record<string, string>,
|
|
103
|
+
).sort(),
|
|
104
|
+
).toEqual(["test/fixture/input1", "test/fixture/input2"]);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("should resolve output with transformOutputPath option", () => {
|
|
108
|
+
const { result } = applyOptions(
|
|
109
|
+
{
|
|
110
|
+
input: ["test/fixture/**/*.js"],
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
transformOutputPath: (output) => `dest/${output.split("/").at(-1)}`,
|
|
114
|
+
},
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
expect(Object.keys(result.input as Record<string, string>).sort()).toEqual([
|
|
118
|
+
"dest/input1",
|
|
119
|
+
"dest/input2",
|
|
120
|
+
]);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("should warn when input is missing", () => {
|
|
124
|
+
const { result, warn } = applyOptions({}, { relative: "./test" });
|
|
125
|
+
|
|
126
|
+
expect(warn).toHaveBeenCalledWith("At least one input is required");
|
|
127
|
+
expect(result).toEqual({});
|
|
128
|
+
});
|
|
129
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Plugin } from "rolldown";
|
|
2
|
+
import createEntries, { type CreateEntriesOptions } from "@/createEntries";
|
|
3
|
+
|
|
4
|
+
const pluginName = "modular-library/rolldown";
|
|
5
|
+
|
|
6
|
+
export type RolldownModularLibraryOptions = CreateEntriesOptions;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* rolldownModularLibrary is a rolldown plugin to use multiple entry points and preserve the directory
|
|
10
|
+
* structure in the dist folder
|
|
11
|
+
*/
|
|
12
|
+
const rolldownModularLibrary = (
|
|
13
|
+
options?: RolldownModularLibraryOptions,
|
|
14
|
+
): Plugin => {
|
|
15
|
+
return {
|
|
16
|
+
name: pluginName,
|
|
17
|
+
options(conf) {
|
|
18
|
+
if (!conf.input) {
|
|
19
|
+
if (this.warn) {
|
|
20
|
+
this.warn("At least one input is required");
|
|
21
|
+
}
|
|
22
|
+
return conf;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const input = createEntries(conf.input, options);
|
|
26
|
+
return {
|
|
27
|
+
...conf,
|
|
28
|
+
input,
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default rolldownModularLibrary;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { rollup, RollupOptions } from "rollup";
|
|
2
|
+
import importJson from "@rollup/plugin-json";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import modularLibrary from "./plugin";
|
|
5
|
+
|
|
6
|
+
const expectedOutput = ["fixture/input1.js", "fixture/input2.js"].sort();
|
|
7
|
+
|
|
8
|
+
const externalDependencies = ["node:fs", "path"];
|
|
9
|
+
|
|
10
|
+
describe.each([
|
|
11
|
+
["rollup 4", rollup],
|
|
12
|
+
])("modular-library/rollup using %s", (_, rollup) => {
|
|
13
|
+
const generateBundle = (options: RollupOptions) =>
|
|
14
|
+
rollup(options).then((bundle) =>
|
|
15
|
+
bundle.generate({
|
|
16
|
+
format: "cjs",
|
|
17
|
+
}),
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const generateOutputFileNames = (options: RollupOptions) =>
|
|
21
|
+
generateBundle(options).then(({ output }) =>
|
|
22
|
+
output.map((module) => module.fileName).sort(),
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
it("should have name modular-library/rollup", async () => {
|
|
26
|
+
const plugin = modularLibrary({ relative: "./test" });
|
|
27
|
+
expect("name" in plugin).toBeTruthy();
|
|
28
|
+
expect(plugin.name).toBe("modular-library/rollup");
|
|
29
|
+
});
|
|
30
|
+
it("should resolve glob", async () => {
|
|
31
|
+
const outputFiles = await generateOutputFileNames({
|
|
32
|
+
input: ["test/fixture/**/*.js"],
|
|
33
|
+
plugins: [modularLibrary({ relative: "./test" })],
|
|
34
|
+
});
|
|
35
|
+
expect(outputFiles).toEqual(expectedOutput);
|
|
36
|
+
});
|
|
37
|
+
it("should accept a simple string as input", async () => {
|
|
38
|
+
const outputFiles = await generateOutputFileNames({
|
|
39
|
+
input: "test/fixture/**/*.js",
|
|
40
|
+
plugins: [modularLibrary({ relative: "./test" })],
|
|
41
|
+
});
|
|
42
|
+
expect(outputFiles).toEqual(expectedOutput);
|
|
43
|
+
});
|
|
44
|
+
it("should accept an array of strings as input", async () => {
|
|
45
|
+
const outputFiles = await generateOutputFileNames({
|
|
46
|
+
input: ["test/fixture/**/*.js"],
|
|
47
|
+
plugins: [modularLibrary({ relative: "./test" })],
|
|
48
|
+
});
|
|
49
|
+
expect(outputFiles).toEqual(expectedOutput);
|
|
50
|
+
});
|
|
51
|
+
it("should remove unresolved glob", async () => {
|
|
52
|
+
const outputFiles = await generateOutputFileNames({
|
|
53
|
+
input: ["test/fixture/**/*.js", "/not-found/file.js"],
|
|
54
|
+
plugins: [modularLibrary({ relative: "./test" })],
|
|
55
|
+
});
|
|
56
|
+
expect(outputFiles).toEqual(expectedOutput);
|
|
57
|
+
});
|
|
58
|
+
it("should preserve no string entries", async () => {
|
|
59
|
+
const bundle = generateBundle({
|
|
60
|
+
// @ts-expect-error
|
|
61
|
+
input: [
|
|
62
|
+
"test/fixture/**/*.js",
|
|
63
|
+
{
|
|
64
|
+
test: "path/to/test.js",
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
plugins: [modularLibrary({ relative: "./test" })],
|
|
68
|
+
});
|
|
69
|
+
await expect(bundle).rejects.toThrow(/^Could not resolve entry module/);
|
|
70
|
+
});
|
|
71
|
+
it('should resolve relative to "src" as default', async () => {
|
|
72
|
+
const outputFilesWithNoOptions = await generateOutputFileNames({
|
|
73
|
+
input: ["test/fixture/**/*.js"],
|
|
74
|
+
plugins: [modularLibrary(), importJson()],
|
|
75
|
+
external: externalDependencies,
|
|
76
|
+
});
|
|
77
|
+
const outputFilesWithNoRelativeOption = await generateOutputFileNames({
|
|
78
|
+
input: ["./test/fixture/**/*.js"],
|
|
79
|
+
plugins: [modularLibrary({}), importJson()],
|
|
80
|
+
external: externalDependencies,
|
|
81
|
+
});
|
|
82
|
+
expect(outputFilesWithNoOptions).toEqual([
|
|
83
|
+
"test/fixture/input1.js",
|
|
84
|
+
"test/fixture/input2.js",
|
|
85
|
+
]);
|
|
86
|
+
expect(outputFilesWithNoRelativeOption).toEqual([
|
|
87
|
+
"test/fixture/input1.js",
|
|
88
|
+
"test/fixture/input2.js",
|
|
89
|
+
]);
|
|
90
|
+
});
|
|
91
|
+
it('should resolve non relative to "relative" options path to root', async () => {
|
|
92
|
+
const outputFiles = await generateOutputFileNames({
|
|
93
|
+
input: ["test/fixture/**/*.js"],
|
|
94
|
+
plugins: [modularLibrary(), importJson()],
|
|
95
|
+
external: ["node:fs", "path"],
|
|
96
|
+
});
|
|
97
|
+
expect(outputFiles).toEqual([
|
|
98
|
+
"test/fixture/input1.js",
|
|
99
|
+
"test/fixture/input2.js",
|
|
100
|
+
]);
|
|
101
|
+
});
|
|
102
|
+
it('should resolve output to "dist" directory', async () => {
|
|
103
|
+
const outputFiles = await generateOutputFileNames({
|
|
104
|
+
input: ["test/fixture/**/*.js"],
|
|
105
|
+
plugins: [
|
|
106
|
+
modularLibrary({
|
|
107
|
+
transformOutputPath: (output) => `dest/${path.basename(output)}`,
|
|
108
|
+
}),
|
|
109
|
+
],
|
|
110
|
+
external: ["node:fs", "path"],
|
|
111
|
+
});
|
|
112
|
+
expect(outputFiles).toEqual(["dest/input1.js", "dest/input2.js"]);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Plugin } from "rollup";
|
|
2
|
+
import createEntries, { CreateEntriesOptions } from "@/createEntries";
|
|
3
|
+
|
|
4
|
+
const pluginName = "modular-library/rollup";
|
|
5
|
+
|
|
6
|
+
export type RollupModularLibraryOptions = CreateEntriesOptions;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* modularLibrary is a rollup plugin to use multiple entry point and preserve the directory
|
|
10
|
+
* structure in the dist folder
|
|
11
|
+
* */
|
|
12
|
+
const rollupModularLibrary = (
|
|
13
|
+
options?: RollupModularLibraryOptions,
|
|
14
|
+
): Plugin => {
|
|
15
|
+
return {
|
|
16
|
+
name: pluginName,
|
|
17
|
+
buildStart() {},
|
|
18
|
+
options(conf) {
|
|
19
|
+
if (!conf.input) {
|
|
20
|
+
if (this.warn) {
|
|
21
|
+
this.warn("At least one input is required");
|
|
22
|
+
}
|
|
23
|
+
return conf;
|
|
24
|
+
}
|
|
25
|
+
const input = createEntries(conf.input, options);
|
|
26
|
+
return {
|
|
27
|
+
...conf,
|
|
28
|
+
input,
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default rollupModularLibrary;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import type { UserConfig } from "vite";
|
|
2
|
+
import modularLibrary from "./plugin";
|
|
3
|
+
|
|
4
|
+
const expectedOutput = ["fixture/input1", "fixture/input2"].sort();
|
|
5
|
+
|
|
6
|
+
const applyConfig = (
|
|
7
|
+
config: UserConfig,
|
|
8
|
+
pluginOptions?: Parameters<typeof modularLibrary>[0],
|
|
9
|
+
) => {
|
|
10
|
+
const plugin = modularLibrary(pluginOptions);
|
|
11
|
+
if (!plugin.config) {
|
|
12
|
+
throw new Error("config hook is required");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const context = {
|
|
16
|
+
warn: vi.fn(),
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// @ts-expect-error
|
|
20
|
+
const result = plugin.config.call(context as never, config);
|
|
21
|
+
return {
|
|
22
|
+
result,
|
|
23
|
+
warn: context.warn,
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
describe("modular-library/vite", () => {
|
|
28
|
+
it("should have name modular-library/vite", () => {
|
|
29
|
+
const plugin = modularLibrary({ relative: "./test" });
|
|
30
|
+
expect(plugin.name).toBe("modular-library/vite");
|
|
31
|
+
expect(plugin.apply).toBe("build");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("should warn when build.lib is missing", () => {
|
|
35
|
+
const { result, warn } = applyConfig({});
|
|
36
|
+
|
|
37
|
+
expect(warn).toHaveBeenCalledWith("The build.lib option is required");
|
|
38
|
+
expect(result).toEqual({});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should warn when build.lib.entry is missing", () => {
|
|
42
|
+
const { result, warn } = applyConfig({
|
|
43
|
+
build: {
|
|
44
|
+
// @ts-expect-error
|
|
45
|
+
lib: {},
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
expect(warn).toHaveBeenCalledWith("At least one entry is required");
|
|
50
|
+
expect(result).toEqual({
|
|
51
|
+
build: {
|
|
52
|
+
lib: {},
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should resolve glob", () => {
|
|
58
|
+
const { result } = applyConfig(
|
|
59
|
+
{
|
|
60
|
+
build: {
|
|
61
|
+
lib: {
|
|
62
|
+
entry: ["test/fixture/**/*.js"],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
{ relative: "./test" },
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
expect(
|
|
70
|
+
Object.keys(
|
|
71
|
+
(result.build?.lib as { entry: Record<string, string> }).entry,
|
|
72
|
+
).sort(),
|
|
73
|
+
).toEqual(expectedOutput);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("should accept a simple string as input", () => {
|
|
77
|
+
const { result } = applyConfig(
|
|
78
|
+
{
|
|
79
|
+
build: {
|
|
80
|
+
lib: {
|
|
81
|
+
entry: "test/fixture/**/*.js",
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
{ relative: "./test" },
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
expect(
|
|
89
|
+
Object.keys(
|
|
90
|
+
(result.build?.lib as { entry: Record<string, string> }).entry,
|
|
91
|
+
).sort(),
|
|
92
|
+
).toEqual(expectedOutput);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("should accept an array of strings as input", () => {
|
|
96
|
+
const { result } = applyConfig(
|
|
97
|
+
{
|
|
98
|
+
build: {
|
|
99
|
+
lib: {
|
|
100
|
+
entry: ["test/fixture/**/*.js"],
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
{ relative: "./test" },
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
expect(
|
|
108
|
+
Object.keys(
|
|
109
|
+
(result.build?.lib as { entry: Record<string, string> }).entry,
|
|
110
|
+
).sort(),
|
|
111
|
+
).toEqual(expectedOutput);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("should remove unresolved glob", () => {
|
|
115
|
+
const { result } = applyConfig(
|
|
116
|
+
{
|
|
117
|
+
build: {
|
|
118
|
+
lib: {
|
|
119
|
+
entry: ["test/fixture/**/*.js", "/not-found/file.js"],
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
{ relative: "./test" },
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
expect(
|
|
127
|
+
Object.keys(
|
|
128
|
+
(result.build?.lib as { entry: Record<string, string> }).entry,
|
|
129
|
+
).sort(),
|
|
130
|
+
).toEqual(expectedOutput);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("should resolve relative to src as default", () => {
|
|
134
|
+
const { result: outputFilesWithNoOptions } = applyConfig({
|
|
135
|
+
build: {
|
|
136
|
+
lib: {
|
|
137
|
+
entry: ["test/fixture/**/*.js"],
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
const { result: outputFilesWithNoRelativeOption } = applyConfig(
|
|
142
|
+
{
|
|
143
|
+
build: {
|
|
144
|
+
lib: {
|
|
145
|
+
entry: ["./test/fixture/**/*.js"],
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
{},
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
expect(
|
|
153
|
+
Object.keys(
|
|
154
|
+
(
|
|
155
|
+
outputFilesWithNoOptions.build?.lib as {
|
|
156
|
+
entry: Record<string, string>;
|
|
157
|
+
}
|
|
158
|
+
).entry,
|
|
159
|
+
).sort(),
|
|
160
|
+
).toEqual(["test/fixture/input1", "test/fixture/input2"]);
|
|
161
|
+
expect(
|
|
162
|
+
Object.keys(
|
|
163
|
+
(
|
|
164
|
+
outputFilesWithNoRelativeOption.build?.lib as {
|
|
165
|
+
entry: Record<string, string>;
|
|
166
|
+
}
|
|
167
|
+
).entry,
|
|
168
|
+
).sort(),
|
|
169
|
+
).toEqual(["test/fixture/input1", "test/fixture/input2"]);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("should resolve output with transformOutputPath option", () => {
|
|
173
|
+
const { result } = applyConfig(
|
|
174
|
+
{
|
|
175
|
+
build: {
|
|
176
|
+
lib: {
|
|
177
|
+
entry: ["test/fixture/**/*.js"],
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
transformOutputPath: (output) => `dest/${output.split("/").at(-1)}`,
|
|
183
|
+
},
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
expect(
|
|
187
|
+
Object.keys(
|
|
188
|
+
(result.build?.lib as { entry: Record<string, string> }).entry,
|
|
189
|
+
).sort(),
|
|
190
|
+
).toEqual(["dest/input1", "dest/input2"]);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
import createEntries, { type CreateEntriesOptions } from "@/createEntries";
|
|
3
|
+
|
|
4
|
+
const pluginName = "modular-library/vite";
|
|
5
|
+
|
|
6
|
+
export type ViteModularLibraryOptions = CreateEntriesOptions;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* viteModularLibrary is a vite plugin to use multiple entry points and preserve the directory
|
|
10
|
+
* structure in the dist folder
|
|
11
|
+
*/
|
|
12
|
+
const viteModularLibrary = (options?: ViteModularLibraryOptions): Plugin => {
|
|
13
|
+
return {
|
|
14
|
+
name: pluginName,
|
|
15
|
+
apply: "build",
|
|
16
|
+
config(config) {
|
|
17
|
+
if (!config.build?.lib) {
|
|
18
|
+
if (this.warn) {
|
|
19
|
+
this.warn("The build.lib option is required");
|
|
20
|
+
}
|
|
21
|
+
return config;
|
|
22
|
+
}
|
|
23
|
+
const entry = config.build?.lib?.entry;
|
|
24
|
+
|
|
25
|
+
if (!entry) {
|
|
26
|
+
if (this.warn) {
|
|
27
|
+
this.warn("At least one entry is required");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return config;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
config.build.lib.entry = createEntries(entry, options);
|
|
34
|
+
|
|
35
|
+
return config;
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export default viteModularLibrary;
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "CommonJS",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"outDir": "dist",
|
|
8
|
+
"rootDir": "src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"skipLibCheck": true,
|
|
13
|
+
"baseUrl": ".",
|
|
14
|
+
"paths": {
|
|
15
|
+
"@/*": ["src/*"]
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"include": ["src", "node_modules/vitest/globals.d.ts"]
|
|
19
|
+
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import react from "@vitejs/plugin-react";
|
|
2
|
+
import dts from "unplugin-dts/vite";
|
|
3
|
+
import { defineConfig } from "vite";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
plugins: [
|
|
8
|
+
react(),
|
|
9
|
+
dts({
|
|
10
|
+
copyDtsFiles: true,
|
|
11
|
+
outDirs: ["dist"],
|
|
12
|
+
exclude: ["src/**/*.test.ts"],
|
|
13
|
+
beforeWriteFile: (filePath, content) => ({
|
|
14
|
+
filePath: filePath.replace(/([\\/])dist\1src\1/, "$1dist$1"),
|
|
15
|
+
content,
|
|
16
|
+
}),
|
|
17
|
+
}),
|
|
18
|
+
],
|
|
19
|
+
build: {
|
|
20
|
+
lib: {
|
|
21
|
+
entry: {
|
|
22
|
+
"rolldown/index": resolve(__dirname, "src/rolldown/index.ts"),
|
|
23
|
+
"rollup/index": resolve(__dirname, "src/rollup/index.ts"),
|
|
24
|
+
"vite/index": resolve(__dirname, "src/vite/index.ts"),
|
|
25
|
+
},
|
|
26
|
+
formats: ["es", "cjs"],
|
|
27
|
+
fileName: (format, entryName) => `${entryName}.${format}.js`,
|
|
28
|
+
},
|
|
29
|
+
rolldownOptions: {
|
|
30
|
+
output: {
|
|
31
|
+
chunkFileNames: "chunks/[name].js",
|
|
32
|
+
},
|
|
33
|
+
external: ["node:path", "node:fs"],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
});
|
package/vitest.config.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineConfig } from "vitest/config";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
resolve: {
|
|
6
|
+
alias: {
|
|
7
|
+
"@": resolve(__dirname, "src"),
|
|
8
|
+
},
|
|
9
|
+
},
|
|
10
|
+
test: {
|
|
11
|
+
environment: "node",
|
|
12
|
+
globals: true,
|
|
13
|
+
coverage: {
|
|
14
|
+
provider: "v8",
|
|
15
|
+
reportsDirectory: "./coverage",
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
});
|