Skip to content

Commit 77a841d

Browse files
committed
squashed
1 parent 232dcf8 commit 77a841d

8 files changed

Lines changed: 188 additions & 98 deletions

File tree

packages/multi-entry/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
},
6262
"dependencies": {
6363
"@rollup/plugin-virtual": "^3.0.0",
64-
"matched": "^5.0.1"
64+
"tinyglobby": "^0.2.14"
6565
},
6666
"devDependencies": {
6767
"rollup": "^4.0.0-24"
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { readFileSync } from 'fs';
22

3+
import typescript from '@rollup/plugin-typescript';
4+
35
import { createConfig } from '../../shared/rollup.config.mjs';
46

57
export default {
68
...createConfig({
79
pkg: JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8'))
810
}),
9-
input: 'src/index.js',
10-
plugins: []
11+
input: 'src/index.ts',
12+
plugins: [typescript()]
1113
};

packages/multi-entry/src/index.js

Lines changed: 0 additions & 79 deletions
This file was deleted.

packages/multi-entry/src/index.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import virtual from '@rollup/plugin-virtual';
2+
import { glob } from 'tinyglobby';
3+
4+
import type { Plugin } from 'rollup';
5+
6+
import type { RollupMultiEntryOptions } from '../types';
7+
8+
import { extractDirectories } from './utils';
9+
10+
const DEFAULT_OUTPUT = 'multi-entry.js';
11+
const AS_IMPORT = 'import';
12+
const AS_EXPORT = 'export * from';
13+
14+
export default function multiEntry(config: RollupMultiEntryOptions = {}): Plugin {
15+
let entryFileName = config.entryFileName ?? DEFAULT_OUTPUT;
16+
let include: string[] = [];
17+
let exclude: string[] = [];
18+
let exports = config.exports ?? true;
19+
20+
const exporter = (path: string) => `${exports ? AS_EXPORT : AS_IMPORT} ${JSON.stringify(path)}`;
21+
22+
let virtualisedEntry: {
23+
resolveId(id: string, importer?: string): string | null;
24+
load(id: string): string | null;
25+
};
26+
27+
return {
28+
name: 'multi-entry',
29+
30+
options(options) {
31+
if (options.input !== entryFileName) {
32+
if (typeof options.input === 'string') {
33+
include = [options.input];
34+
} else if (Array.isArray(options.input)) {
35+
include = options.input;
36+
} else if (options.input) {
37+
// Consider options.input as a configuration object for this plugin instead
38+
// of an `{ [entryAlias: string]: string; }` map object
39+
const input = options.input as RollupMultiEntryOptions;
40+
entryFileName = input.entryFileName ?? DEFAULT_OUTPUT;
41+
include = typeof input.include === 'string' ? [input.include] : input.include ?? [];
42+
exclude = typeof input.exclude === 'string' ? [input.exclude] : input.exclude ?? [];
43+
exports = input.exports ?? true;
44+
}
45+
}
46+
47+
return {
48+
...options,
49+
input: entryFileName
50+
};
51+
},
52+
53+
outputOptions(options) {
54+
return {
55+
...options,
56+
entryFileNames: config.preserveModules ? options.entryFileNames : entryFileName
57+
};
58+
},
59+
60+
async buildStart(options) {
61+
const patterns = include.concat(exclude.map((pattern) => `!${pattern}`));
62+
const entries = patterns.length
63+
? glob(patterns, { absolute: true })
64+
.then((paths) => paths.sort())
65+
.then((paths) => paths.map(exporter).join('\n'))
66+
: Promise.resolve('');
67+
virtualisedEntry = virtual({ [options.input as unknown as string]: await entries }) as any;
68+
69+
if (this.meta.watchMode) {
70+
for (const dir of extractDirectories(patterns)) this.addWatchFile(dir);
71+
}
72+
},
73+
74+
resolveId(id, importer) {
75+
return virtualisedEntry && virtualisedEntry.resolveId(id, importer);
76+
},
77+
78+
load(id) {
79+
return virtualisedEntry && virtualisedEntry.load(id);
80+
}
81+
};
82+
}

packages/multi-entry/src/utils.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/* eslint-disable no-irregular-whitespace, no-continue */
2+
import { isDynamicPattern } from 'tinyglobby';
3+
4+
/**
5+
* Transforms an array of patterns into an array of static directories.
6+
*
7+
* @example
8+
* ["./src​/**​/*.js"] -> ["./src"]
9+
* ["./{lib,utils}/index.js"] -> ["."]
10+
*/
11+
export function extractDirectories(patterns: string[]): string[] {
12+
const directories = new Set<string>();
13+
14+
for (const pattern of patterns) {
15+
// Skip negated patterns
16+
if (pattern.startsWith('!')) continue;
17+
18+
const parts = pattern.split(/\/|\\/g);
19+
let [dir] = parts;
20+
21+
// If the pattern is dynamic from the beginning, skip it
22+
if (isDynamicPattern(dir)) continue;
23+
24+
// Join all the parts until the pattern is dynamic
25+
for (const part of parts.slice(1)) {
26+
const newDir = `${dir}/${part}`;
27+
if (isDynamicPattern(newDir)) {
28+
directories.add(dir);
29+
break;
30+
}
31+
dir = newDir;
32+
}
33+
directories.add(dir);
34+
}
35+
36+
return [...directories];
37+
}

packages/multi-entry/test/test.mjs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,40 @@ test('deterministic output, regardless of input order', async (t) => {
147147

148148
t.is(code1, code2);
149149
});
150+
151+
test('correctly extracts watch directories from glob patterns', async (t) => {
152+
const plugin = multiEntry();
153+
const options = plugin.options({
154+
input: ['test/fixtures/*.js', 'src/**/*.js', './lib/{util,helper}.js']
155+
});
156+
157+
const watchedDirs = [];
158+
await plugin.buildStart.call(
159+
{
160+
meta: { watchMode: true },
161+
addWatchFile: (dir) => {
162+
watchedDirs.push(dir);
163+
}
164+
},
165+
options
166+
);
167+
168+
t.deepEqual(watchedDirs, ['test/fixtures', 'src', './lib']);
169+
});
170+
171+
test('does not watch directories when not in watch mode', async (t) => {
172+
const plugin = multiEntry();
173+
const options = plugin.options({ input: 'test/fixtures/*.js' });
174+
175+
await plugin.buildStart.call(
176+
{
177+
meta: { watchMode: false },
178+
addWatchFile: () => {
179+
t.fail('Should not call addWatchFile when not in watch mode');
180+
}
181+
},
182+
options
183+
);
184+
185+
t.pass('Should not attempt to watch files when not in watch mode');
186+
});

packages/multi-entry/types/index.d.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
1-
import type { FilterPattern } from '@rollup/pluginutils';
21
import type { Plugin } from 'rollup';
32

4-
interface RollupMultiEntryOptions {
3+
export interface RollupMultiEntryOptions {
54
/**
65
* A minimatch pattern, or array of patterns, which specifies the files in the build the plugin
76
* should operate on.
87
* By default all files are targeted.
98
*/
10-
include?: FilterPattern;
9+
include?: string | string[];
1110
/**
1211
* A minimatch pattern, or array of patterns, which specifies the files in the build the plugin
1312
* should _ignore_.
1413
* By default no files are ignored.
1514
*/
16-
exclude?: FilterPattern;
15+
exclude?: string | string[];
1716
/**
1817
* - If `true`, instructs the plugin to export named exports to the bundle from all entries.
1918
* - If `false`, the plugin will not export any entry exports to the bundle.

pnpm-lock.yaml

Lines changed: 24 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)