Skip to content

Commit 34c0d38

Browse files
committed
Ask git the root of the repo instead of (wrongly) looking for a .git folder
1 parent fd256ae commit 34c0d38

File tree

3 files changed

+35
-20
lines changed

3 files changed

+35
-20
lines changed

source/index.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import path from 'node:path'
22
import fs from 'node:fs/promises'
3+
import cp from 'node:child_process'
34
import { createRequire, isBuiltin } from 'node:module'
45
import type { Plugin } from 'rollup'
56

@@ -98,7 +99,7 @@ const { name, version } = createRequire(import.meta.url)('#package.json') as Pac
9899
// Files that mark the root of a monorepo
99100
const workspaceRootFiles = [
100101
'pnpm-workspace.yaml', // pnpm
101-
'lerna.json', // Lerna
102+
'lerna.json', // Lerna / Lerna Light
102103
// Note: is there any interest in the following?
103104
// 'workspace.jsonc', // Bit
104105
// 'nx.json', // Nx
@@ -126,7 +127,7 @@ const isString = (str: unknown): str is string =>
126127
* A Rollup/Vite plugin that automatically declares NodeJS built-in modules,
127128
* and optionally npm dependencies, as 'external'.
128129
*/
129-
function nodeExternals(options: ExternalsOptions = {}): Plugin {
130+
async function nodeExternals(options: ExternalsOptions = {}): Promise<Plugin> {
130131

131132
const config: Config = { ...defaults, ...options }
132133

@@ -136,6 +137,13 @@ function nodeExternals(options: ExternalsOptions = {}): Plugin {
136137
const isIncluded = (id: string) => include.length > 0 && include.some(rx => rx.test(id)),
137138
isExcluded = (id: string) => exclude.length > 0 && exclude.some(rx => rx.test(id))
138139

140+
// Determine the root of the git repository, if any.
141+
const gitRepository = await new Promise<string | null>(resolve => {
142+
cp.execFile('git', [ 'rev-parse', '--show-toplevel' ], (error, stdout) => {
143+
return resolve(error ? null : path.normalize(stdout.trim()))
144+
})
145+
})
146+
139147
return {
140148
name: name.replace(/^rollup-plugin-/, ''),
141149
version,
@@ -165,29 +173,24 @@ function nodeExternals(options: ExternalsOptions = {}): Plugin {
165173
.concat(config.packagePath)
166174
.filter(isString)
167175
.map(packagePath => path.resolve(packagePath))
176+
168177
if (packagePaths.length === 0) {
169-
search: for (
170-
let current = process.cwd(), previous: string | undefined = undefined;
171-
previous !== current;
172-
previous = current, current = path.dirname(current)
173-
) {
178+
search:
179+
for (let current = process.cwd(), previous: string | undefined = undefined; previous !== current; previous = current, current = path.dirname(current)) {
180+
174181
// Gather package.json files.
175182
let name = path.join(current, 'package.json')
176183
let stat = await fs.stat(name).catch(() => null)
177184
if (stat?.isFile())
178185
packagePaths.push(name)
179186

180187
// Break early if we are at the root of a git repo.
181-
name = path.join(current, '.git')
182-
stat = await fs.stat(name).catch(() => null)
183-
if (stat?.isDirectory())
188+
if (current === gitRepository)
184189
break
185190

186191
// Break early is there is a known workspace root file.
187192
for (const file of workspaceRootFiles) {
188-
name = path.join(current, file)
189-
stat = await fs.stat(name).catch(() => null)
190-
if (stat?.isFile())
193+
if (await fs.stat(path.join(current, file)).then(stat => stat.isFile()).catch(() => false))
191194
break search
192195
}
193196
}

test/_common.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class MockPluginContext {
5454
}
5555

5656
export async function initPlugin(options: ExternalsOptions = {}) {
57-
const plugin = nodeExternals(options)
57+
const plugin = await nodeExternals(options)
5858
const context = new MockPluginContext(plugin)
5959
await context.buildStart()
6060
return context

test/monorepo.test.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,26 @@
1+
import { promisify } from 'node:util'
2+
import cp from 'node:child_process'
13
import fs from 'node:fs/promises'
24
import test from 'ava'
35
import { initPlugin, fixture } from './_common.ts'
46

5-
// These two tests need to be run in sequence
7+
// The two tests in this file need to be run in sequence so one does not interfere with the other
8+
69
test.serial('git monorepo usage', async t => {
7-
await fs.mkdir(fixture('01_monorepo/.git'), { recursive: true })
8-
process.chdir(fixture('01_monorepo/one'))
10+
11+
t.log('Creating temporary git repo...')
12+
process.chdir(fixture('01_monorepo'))
13+
const execFile = promisify(cp.execFile)
14+
await execFile('git', [ 'init' ] ).catch(() => {})
15+
16+
t.teardown(async () => {
17+
t.log('Removing temporary git repo...')
18+
process.chdir(fixture('01_monorepo'))
19+
await fs.rm('.git', { recursive: true, force: true })
20+
})
921

1022
// Should gather dependencies up to ./test/fixtures/01_monorepo
23+
process.chdir(fixture('01_monorepo/one'))
1124
const context = await initPlugin()
1225

1326
// Should be external
@@ -28,10 +41,9 @@ test.serial('git monorepo usage', async t => {
2841
})
2942

3043
test.serial('non-git monorepo usage', async t => {
31-
await fs.rmdir(fixture('01_monorepo/.git'))
32-
process.chdir(fixture('01_monorepo/one'))
3344

34-
// Should gather dependencies up to . !
45+
// Should gather dependencies up to / !
46+
process.chdir(fixture('01_monorepo/one'))
3547
const context = await initPlugin()
3648

3749
// Should be external

0 commit comments

Comments
 (0)