Skip to content

Commit de95af0

Browse files
authored
Merge pull request #186 from DataDog/yoann/fix-vite-outdir-sourcemaps
[fix] Vite outDir for sourcemaps upload (take 2)
2 parents cb65ff9 + c8a6c27 commit de95af0

9 files changed

Lines changed: 1065 additions & 204 deletions

File tree

packages/plugins/bundler-report/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"@dd/core": "workspace:*"
2323
},
2424
"devDependencies": {
25+
"rollup": "4.24.2",
2526
"typescript": "5.4.3"
2627
}
2728
}
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
// Unless explicitly stated otherwise all files in this repository are licensed under the MIT License.
2+
// This product includes software developed at Datadog (https://www.datadoghq.com/).
3+
// Copyright 2019-Present Datadog, Inc.
4+
5+
import { addFixtureFiles } from '@dd/tests/_jest/helpers/mocks';
6+
import type { InputOptions } from 'rollup';
7+
8+
import { computeCwd, getOutDirFromOutputs } from './rollup';
9+
10+
jest.mock('@dd/core/helpers/fs', () => {
11+
const original = jest.requireActual('@dd/core/helpers/fs');
12+
return {
13+
...original,
14+
existsSync: jest.fn(),
15+
};
16+
});
17+
18+
describe('Rollup Helpers', () => {
19+
describe('getOutDirFromOutputs', () => {
20+
const cases = [
21+
{
22+
description: 'extract dir from single output object with dir',
23+
outputOptions: { dir: 'dist' },
24+
expected: 'dist',
25+
},
26+
{
27+
description: 'extract dir from single output object with file',
28+
outputOptions: { file: 'dist/bundle.js' },
29+
expected: 'dist',
30+
},
31+
{
32+
description: 'extract dir from array of outputs with dir',
33+
outputOptions: [{ dir: 'dist' }, { dir: 'dist2' }],
34+
expected: 'dist',
35+
},
36+
{
37+
description: 'extract dir from array of outputs with file',
38+
outputOptions: [{ file: 'dist/bundle.js' }, { file: 'dist2/bundle.js' }],
39+
expected: 'dist',
40+
},
41+
{
42+
description: 'prefer dir over file in same output',
43+
outputOptions: { dir: 'dist', file: 'other/bundle.js' },
44+
expected: 'dist',
45+
},
46+
{
47+
description: 'handle nested file paths',
48+
outputOptions: { file: 'dist/assets/js/bundle.js' },
49+
expected: 'dist/assets/js',
50+
},
51+
{
52+
description: 'return undefined for no outputOptions',
53+
outputOptions: undefined,
54+
expected: undefined,
55+
},
56+
{
57+
description: 'return undefined for empty array',
58+
outputOptions: [],
59+
expected: undefined,
60+
},
61+
{
62+
description: 'return undefined when no dir or file specified',
63+
outputOptions: [{ format: 'esm' }, { format: 'cjs' }],
64+
expected: undefined,
65+
},
66+
];
67+
68+
test.each(cases)('Should $description', ({ outputOptions, expected }) => {
69+
// Rollup doesn't type InputOptions['output'], yet it exists.
70+
expect(getOutDirFromOutputs({ output: outputOptions } as InputOptions)).toBe(expected);
71+
});
72+
});
73+
74+
describe('computeCwd', () => {
75+
beforeAll(() => {
76+
jest.spyOn(process, 'cwd').mockReturnValue('/base/cwd');
77+
});
78+
79+
beforeEach(() => {
80+
// Set up virtual file system for package.json files
81+
addFixtureFiles({
82+
'/project/package.json': '',
83+
'/project/src/package.json': '',
84+
'/project/lib/package.json': '',
85+
'/base/cwd/package.json': '',
86+
});
87+
});
88+
89+
const cases = [
90+
{
91+
description: 'handle string input',
92+
options: { input: '/project/src/index.js' },
93+
expected: '/project',
94+
},
95+
{
96+
description: 'handle array input',
97+
options: {
98+
input: ['/project/src/index.js', '/project/lib/util.js'],
99+
},
100+
expected: '/project',
101+
},
102+
{
103+
description: 'handle object input',
104+
options: {
105+
input: {
106+
main: '/project/src/index.js',
107+
util: '/project/lib/util.js',
108+
},
109+
},
110+
expected: '/project',
111+
},
112+
{
113+
description: 'throw error for invalid input type in object',
114+
options: { input: { main: 123 } },
115+
shouldThrow: 'Invalid input type',
116+
},
117+
{
118+
description: 'throw error for invalid input type in array',
119+
options: { input: [123] },
120+
shouldThrow: 'Invalid input type',
121+
},
122+
{
123+
description: 'include absolute output directory in cwd computation',
124+
options: {
125+
input: '/project/src/index.js',
126+
output: { dir: '/project/dist' },
127+
},
128+
expected: '/project',
129+
},
130+
{
131+
description: 'ignore relative output directory',
132+
options: {
133+
input: '/project/src/index.js',
134+
output: { dir: 'dist' },
135+
},
136+
expected: '/project',
137+
},
138+
{
139+
description: 'fallback to process.cwd when no input',
140+
options: {},
141+
expected: '/base/cwd',
142+
},
143+
{
144+
description: 'fallback to process.cwd with relative input',
145+
options: { input: 'index.js' },
146+
expected: '/base/cwd',
147+
},
148+
];
149+
150+
test.each(cases)('Should $description', ({ options, expected, shouldThrow }) => {
151+
const errors = [];
152+
const results = [];
153+
const expectedResults = expected ? [expected] : [];
154+
const expectedErrors = shouldThrow ? [shouldThrow] : [];
155+
156+
try {
157+
// Rollup doesn't type InputOptions['output'], yet it exists.
158+
const result = computeCwd(options as InputOptions);
159+
results.push(result);
160+
} catch (error: any) {
161+
errors.push(error.message);
162+
}
163+
164+
expect(errors).toEqual(expectedErrors);
165+
expect(results).toEqual(expectedResults);
166+
});
167+
});
168+
});
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Unless explicitly stated otherwise all files in this repository are licensed under the MIT License.
2+
// This product includes software developed at Datadog (https://www.datadoghq.com/).
3+
// Copyright 2019-Present Datadog, Inc.
4+
5+
import { getHighestPackageJsonDir, getNearestCommonDirectory } from '@dd/core/helpers/paths';
6+
import path from 'path';
7+
import type { InputOptions } from 'rollup';
8+
9+
// Compute the CWD based on a list of directories.
10+
const getCwd = (dirs: Set<string>) => {
11+
const dirsToUse: Set<string> = new Set(dirs);
12+
for (const dir of dirs) {
13+
const highestPackage = getHighestPackageJsonDir(dir);
14+
if (highestPackage && !dirs.has(highestPackage)) {
15+
dirsToUse.add(highestPackage);
16+
}
17+
}
18+
19+
// Fall back to the nearest common directory.
20+
const nearestDir = getNearestCommonDirectory(Array.from(dirsToUse));
21+
if (nearestDir === path.sep) {
22+
return undefined;
23+
}
24+
return nearestDir;
25+
};
26+
27+
export const getOutDirFromOutputs = (options: InputOptions) => {
28+
const hasOutput = 'output' in options && options.output;
29+
if (!hasOutput) {
30+
return undefined;
31+
}
32+
33+
const outputOptions = options.output;
34+
const normalizedOutputOptions = Array.isArray(outputOptions) ? outputOptions : [outputOptions];
35+
36+
// FIXME: This is an oversimplification, we should handle builds with multiple outputs.
37+
// Ideally, `outDir` should only be computed for the build-report.
38+
// And build-report should also handle multiple outputs.
39+
for (const output of normalizedOutputOptions) {
40+
if (output.dir) {
41+
return output.dir;
42+
}
43+
if (output.file) {
44+
return path.dirname(output.file);
45+
}
46+
}
47+
};
48+
49+
export const computeCwd = (options: InputOptions) => {
50+
const directoriesForCwd: Set<string> = new Set();
51+
52+
if (options.input) {
53+
const normalizedInput = Array.isArray(options.input)
54+
? options.input
55+
: typeof options.input === 'object'
56+
? Object.values(options.input)
57+
: [options.input];
58+
59+
for (const input of normalizedInput) {
60+
if (typeof input !== 'string') {
61+
throw new Error('Invalid input type');
62+
}
63+
directoriesForCwd.add(path.dirname(input));
64+
}
65+
}
66+
67+
// In case an absolute path has been provided in the output options,
68+
// we include it in the directories list for CWD computation.
69+
const outDirFromOutputs = getOutDirFromOutputs(options);
70+
if (outDirFromOutputs && path.isAbsolute(outDirFromOutputs)) {
71+
directoriesForCwd.add(outDirFromOutputs);
72+
}
73+
74+
const cwd = getCwd(directoriesForCwd);
75+
76+
if (cwd) {
77+
return cwd;
78+
}
79+
80+
// Fallback to process.cwd() as would Vite and Rollup do in their own process.
81+
return process.cwd();
82+
};

0 commit comments

Comments
 (0)