Skip to content

Commit bb894a9

Browse files
committed
added support for custom path for each target
1 parent a9856b3 commit bb894a9

7 files changed

Lines changed: 91 additions & 37 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,12 @@ The following configuration options allow you to customize or override the defau
100100
- **`lib`** (`object`): Library configuration settings for both ESM and CJS builds:
101101
- **`esm`** (`object`): Settings for ESM (EcmaScript Module) format output.
102102
- **`output`** (`object`): Configures ESM output settings.
103+
- **`path`** (`string`): Custom output directory path for ESM format (e.g., `'esm'`, `'modern'`). Defaults to root of buildPath.
103104
- **`comments`** (`boolean`): Includes comments in the output if `true`.
104105
- **`sourceMap`** (`boolean`): Generates source maps if `true`.
105106
- **`cjs`** (`object`): Settings for CJS (CommonJS) format output.
106107
- **`output`** (`object`): Configures CJS output settings.
108+
- **`path`** (`string`): Custom output directory path for CJS format (e.g., `'cjs'`, `'legacy'`). Defaults to `'cjs'` in buildPath.
107109
- **`comments`** (`boolean`): Includes comments in the output if `true`.
108110
- **`sourceMap`** (`boolean`): Generates source maps if `true`.
109111
- **`target`** (`string` or `array`): Specifies build targets, such as `"esm"`, `"cjs"`, or an array with both.
@@ -164,12 +166,14 @@ module.exports = {
164166
lib: {
165167
esm: {
166168
output: {
169+
path: 'esm', // custom path for ESM output (defaults to root of buildPath)
167170
comments: true,
168171
sourceMap: true,
169172
},
170173
},
171174
cjs: {
172175
output: {
176+
path: 'cjs', // custom path for CJS output (default cjs inside buildPath)
173177
comments: true,
174178
sourceMap: true,
175179
},

src/config/config.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@ function getInitialConfig(cliProps?: CliProps) {
2424
lib: {
2525
esm: {
2626
output: {
27+
path: './',
2728
comments: true,
2829
sourceMap: Boolean(cliProps.sourceMaps),
2930
},
3031
},
3132
cjs: {
3233
output: {
34+
path: './cjs',
3335
comments: true,
3436
sourceMap: Boolean(cliProps.sourceMaps),
3537
},
@@ -187,7 +189,6 @@ export async function initConfig(cliProps?: CliProps): Promise<Config> {
187189
'**/*.test.tsx',
188190
'**/*.spec.ts',
189191
'**/*.spec.tsx',
190-
'**/*.d.ts',
191192
];
192193
}
193194

@@ -224,6 +225,16 @@ export async function initConfig(cliProps?: CliProps): Promise<Config> {
224225
config.buildPath = path.resolve(root, './dist');
225226
}
226227

228+
// resolve output paths w.r.t buildPath with defaults
229+
config.lib.esm.output.path = path.resolve(
230+
config.buildPath,
231+
config.lib.esm.output.path,
232+
);
233+
config.lib.cjs.output.path = path.resolve(
234+
config.buildPath,
235+
config.lib.cjs.output.path,
236+
);
237+
227238
store.setConfig(config);
228239
return config as Config;
229240
}

src/config/schema.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export interface ModuleConfig {
1010
output?: {
1111
comments?: boolean;
1212
sourceMap?: boolean;
13+
path?: string;
1314
};
1415
}
1516

@@ -22,6 +23,10 @@ export const ModuleConfigSchema = z
2223
.optional()
2324
.describe('Include comments in the output'),
2425
sourceMap: z.boolean().optional().describe('Generate source maps'),
26+
path: z
27+
.string()
28+
.optional()
29+
.describe('Custom output directory path for this module format'),
2530
})
2631
.strict()
2732
.optional()

src/core/build.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ function getAllSourceFiles() {
1717
}
1818

1919
try {
20+
const hasDts = ignore.includes('**/*.d.ts');
2021
return globSync(globPattern, {
2122
cwd: srcPath,
22-
ignore,
23+
// ignore d.ts as its not needed to be transpiled
24+
ignore: [...ignore, !hasDts && '**/*.d.ts'].filter(Boolean),
2325
});
2426
} catch (err) {
2527
console.error(err);

src/core/postbuild.ts

Lines changed: 53 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,22 @@ import { log, parallel, sequential, createTimer } from '@/utils';
99
* Copy required files of module in there folder
1010
*/
1111
export async function copyRequiredFiles() {
12-
const { assets, srcPath, buildPath } = getConfig();
13-
if (!assets || (Array.isArray(assets) && Boolean(assets.length))) {
12+
const { assets, srcPath, buildPath, lib } = getConfig();
13+
if (!assets || (Array.isArray(assets) && assets.length === 0)) {
1414
return;
1515
}
1616

1717
if (!(await fse.pathExists(buildPath))) {
18-
log.warn(`[assets] path ${buildPath} does not exists to copy`);
18+
log.warn(`build path ${buildPath} does not exists to copy assets`);
1919
return [];
2020
}
2121

22-
const hasCJS = await fse.pathExists(`${buildPath}/cjs`);
23-
const hasESM = await fse.pathExists(`${buildPath}/esm`);
22+
// get custom paths from config or use defaults
23+
const cjsPath = lib?.cjs?.output?.path;
24+
const esmPath = lib?.esm?.output?.path;
25+
26+
const hasCJS = await fse.pathExists(cjsPath);
27+
const hasESM = await fse.pathExists(esmPath);
2428

2529
const files = await glob(assets, {
2630
cwd: srcPath,
@@ -32,29 +36,26 @@ export async function copyRequiredFiles() {
3236
files.forEach(file => {
3337
if (hasCJS) {
3438
task.push(
35-
fse.copy(
36-
path.resolve(srcPath, file),
37-
path.resolve(`${buildPath}/cjs`, file),
38-
),
39+
fse.copy(path.resolve(srcPath, file), path.resolve(cjsPath, file)),
3940
);
4041
}
4142
if (hasESM) {
4243
task.push(
43-
fse.copy(
44-
path.resolve(srcPath, file),
45-
path.resolve(`${buildPath}/esm`, file),
46-
),
44+
fse.copy(path.resolve(srcPath, file), path.resolve(esmPath, file)),
4745
);
4846
}
49-
task.push(
50-
fse.copy(path.resolve(srcPath, file), path.resolve(buildPath, file)),
51-
);
5247
});
5348
await Promise.all(task);
5449
}
5550

5651
async function postbuild(getBuildTime: ReturnType<typeof createTimer>) {
57-
const { root, srcPath, buildPath } = getConfig();
52+
const { root, srcPath, buildPath, lib } = getConfig();
53+
54+
const cjsPath = lib?.cjs?.output?.path;
55+
const esmPath = lib?.esm?.output?.path;
56+
57+
const hasCJS = fse.pathExistsSync(cjsPath);
58+
const hasESM = fse.pathExistsSync(esmPath);
5859

5960
/**
6061
* Following function help to move project files like
@@ -73,9 +74,24 @@ async function postbuild(getBuildTime: ReturnType<typeof createTimer>) {
7374
}
7475

7576
/**
76-
* used to create tree-shakeable package.json in each module of project
77+
* createModulePackages used to create tree-shakeable package.json in each module of project
78+
* in case of esm and cjs co exist and esm path is in root then we create module packages
79+
* ```js
80+
* import { someModule } from 'your-lib';
81+
* const { someModule } = require('your-lib');
82+
*
83+
* import someModule from 'your-lib/some-module';
84+
* const someModule = require('your-lib/some-module');
85+
* ```
7786
*/
7887
async function createModulePackages() {
88+
// in case esm and cjs co exist then we create module packages
89+
if (!(hasESM && hasCJS)) return;
90+
91+
// if esm path is not in root then we don't create module packages
92+
// in case or root it will be empty string which is falsy
93+
if (path.relative(buildPath, esmPath)) return;
94+
7995
const packageData = JSON.parse(
8096
await fse.readFile(path.resolve(root, './package.json'), 'utf8'),
8197
);
@@ -92,11 +108,15 @@ async function postbuild(getBuildTime: ReturnType<typeof createTimer>) {
92108
'package.json',
93109
);
94110

95-
const packageJson: Record<string, any> = {
111+
const esmDir = path.join(esmPath, directoryPackage);
112+
const cjsDir = path.join(cjsPath, directoryPackage);
113+
114+
const packageJson = {
115+
name: `${packageData.name}/${directoryPackage}`,
96116
version: packageData.version,
97117
sideEffects: false,
98118
module: './index.js',
99-
main: path.posix.join('../cjs', directoryPackage, 'index.js'),
119+
main: path.posix.join(path.relative(esmDir, cjsDir), 'index.js'),
100120
types: './index.d.ts',
101121
};
102122

@@ -167,14 +187,17 @@ async function postbuild(getBuildTime: ReturnType<typeof createTimer>) {
167187
fse.readFileSync(path.resolve(root, './package.json'), 'utf8'),
168188
);
169189

190+
const cjsDir = path.relative(buildPath, cjsPath);
191+
const esmDir = path.relative(buildPath, esmPath);
192+
170193
const newPackageData = {
171194
...restPackageData,
172195
private: false,
173-
main: fse.existsSync(path.resolve(buildPath, './cjs/index.js'))
174-
? './cjs/index.js'
196+
main: fse.existsSync(path.join(cjsPath, 'index.js'))
197+
? `.${cjsDir ? `/${cjsDir}` : ''}/index.js`
175198
: './index.js',
176-
module: fse.existsSync(path.resolve(buildPath, './esm/index.js'))
177-
? './esm/index.js'
199+
module: fse.existsSync(path.join(esmPath, 'index.js'))
200+
? `.${esmDir ? `/${esmDir}` : ''}/index.js`
178201
: './index.js',
179202
};
180203

@@ -190,13 +213,13 @@ async function postbuild(getBuildTime: ReturnType<typeof createTimer>) {
190213
delete newPackageData.main;
191214
}
192215

193-
const hasDefinitionsFile = fse.pathExistsSync(
194-
path.resolve(buildPath, './index.d.ts'),
195-
);
196-
197-
if (hasDefinitionsFile) {
198-
newPackageData.types = './index.d.ts';
216+
// if esm path exists then by default types will be in esm path else in cjs path
217+
const dtsIndex = path.resolve(hasESM ? esmPath : cjsPath, './index.d.ts');
218+
// if dts file exists then add it to package.json
219+
if (fse.pathExistsSync(dtsIndex)) {
220+
newPackageData.types = `./${path.relative(buildPath, dtsIndex)}`;
199221
}
222+
200223
fse.writeFileSync(
201224
path.resolve(buildPath, './package.json'),
202225
JSON.stringify(newPackageData, null, 2),

src/core/types/types.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,20 @@ function getParsedTSConfig() {
6868
}
6969

7070
function compileDTS(onlyTypeCheck = false) {
71+
const { srcPath, buildPath, lib } = getConfig();
7172
const ts = getTypescript();
72-
const { srcPath, buildPath } = getConfig();
7373
const config = getParsedTSConfig();
7474

75+
const esmDistPath = lib?.esm?.output?.path;
76+
const cjsDistPath = lib?.cjs?.output?.path;
77+
78+
let outDir = buildPath;
79+
if (fse.existsSync(esmDistPath)) {
80+
outDir = esmDistPath;
81+
} else if (fse.existsSync(cjsDistPath)) {
82+
outDir = cjsDistPath;
83+
}
84+
7585
const finalConfig: ParsedCommandLine = {
7686
...config,
7787
options: {
@@ -80,8 +90,8 @@ function compileDTS(onlyTypeCheck = false) {
8090
declaration: true,
8191
noEmit: onlyTypeCheck,
8292
emitDeclarationOnly: true,
83-
outDir: onlyTypeCheck ? undefined : buildPath,
8493
rootDir: srcPath,
94+
outDir: onlyTypeCheck ? undefined : outDir,
8595
},
8696
};
8797

src/transpiler/transpiler.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,14 @@ export const transformFilesAsync = async (
8989
}
9090

9191
let moduleConfig: ModuleConfig;
92-
let outPath = './';
93-
9492
if (target === 'cjs') {
9593
moduleConfig = lib.cjs as ModuleConfig;
96-
outPath = './cjs';
9794
} else if (target === 'esm') {
9895
moduleConfig = lib.esm as ModuleConfig;
9996
}
10097

98+
const outPath = moduleConfig?.output?.path;
99+
101100
const outDir = path.resolve(
102101
buildPath,
103102
// It will support top level path

0 commit comments

Comments
 (0)