Sitelet https://github.com/rollup/rollup/commit/c60770d7aaf750e512c1b2774989ea4596e660b2
Skip to content

Commit c60770d

Browse files
authored
Validate bundle stays within output dir (#6275)
* Validate bundle stays within output dir When a file would leave the output dir, an error is thrown. * Update audit-resolve again * Update agent instructions
1 parent 33f39c1 commit c60770d

36 files changed

Lines changed: 588 additions & 183 deletions

File tree

‎.github/copilot-instructions.md‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,24 @@ When adding/modifying functions that cross the JS-Rust boundary:
3636
- Focus on performance, avoid unnecessary copying of data or AST loops, build the final buffer once
3737
- When adding headers, reserve space for them once upfront to avoid the need to change all existing buffer references
3838

39+
## Browser Path Shim
40+
41+
- `browser/src/path.ts` replaces `node:path` in the browser build (wired via `rollup.config.ts` aliases)
42+
- `src/utils/relativeId.ts` imports `relative` **directly** from `../../browser/src/path` (not via `src/utils/path.ts`) so it works in both builds
43+
- `src/utils/path.ts` re-exports from `node:path`; for browser builds rollup.config.ts substitutes `browser/src/path.ts` transparently for all other imports
44+
3945
## Development Workflow
4046

4147
### Build Outputs
4248

4349
- **Node build**: Artifacts placed in `dist/` (JavaScript + `.node` native modules)
44-
- **Browser build**: Artifacts placed in `browser/dist/`
50+
- **Browser build**: Artifacts placed in `browser/dist/`; browser tests use `browser/dist/rollup.browser.js`
4551
- All tests import from these dist folders - tests run against the full built artifact only
4652

4753
### Quick Rebuild Commands
4854

4955
- `npm run build:quick` - Rebuild both JavaScript and Rust for Node build, copy to dist/
50-
- `npm run update:js` - Rebuild only JavaScript for Node, copy native to dist/
56+
- `npm run update:js` - Rebuild only JavaScript for both Node (`dist/`) and browser (`browser/dist/`), then copy native to dist/; run this after any change to `src/` or `browser/src/`
5157
- `npm run update:napi` - Rebuild only Rust NAPI, copy to dist/
5258
- `npm run build:copy-native` - Copy Rust `.node` files to dist/ (called internally by update commands)
5359

‎audit-resolve.json‎

Lines changed: 43 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,89 @@
11
{
22
"decisions": {
3-
"1113214": {
4-
"decision": "ignore",
5-
"madeAt": 1771566169457,
6-
"expiresAt": 1774158145442
7-
},
8-
"1113296": {
9-
"decision": "ignore",
10-
"madeAt": 1771566180086,
11-
"expiresAt": 1774158145442
12-
},
13-
"1113214|@eslint/js>eslint>@eslint-community/eslint-utils>ajv": {
14-
"decision": "ignore",
15-
"madeAt": 1771567906111,
16-
"expiresAt": 1774159894900
17-
},
18-
"1113214|@typescript-eslint/eslint-plugin>@typescript-eslint/parser>@typescript-eslint/typescript-estree>minimatch>eslint>@eslint-community/eslint-utils>ajv": {
3+
"1113296|@typescript-eslint/eslint-plugin>@typescript-eslint/parser>@typescript-eslint/typescript-estree>minimatch": {
194
"decision": "ignore",
20-
"madeAt": 1771567906111,
5+
"madeAt": 1771567910429,
216
"expiresAt": 1774159894900
227
},
23-
"1113214|@typescript-eslint/type-utils>@typescript-eslint/typescript-estree>minimatch>@typescript-eslint/utils>@eslint-community/eslint-utils>eslint>ajv": {
8+
"1113296|@typescript-eslint/type-utils>@typescript-eslint/typescript-estree>minimatch": {
249
"decision": "ignore",
25-
"madeAt": 1771567906111,
10+
"madeAt": 1771567910429,
2611
"expiresAt": 1774159894900
2712
},
28-
"1113214|eslint>@eslint-community/eslint-utils>ajv": {
13+
"1113296|eslint-plugin-vue>@eslint-community/eslint-utils>eslint>ajv>@typescript-eslint/parser>@typescript-eslint/typescript-estree>minimatch": {
2914
"decision": "ignore",
30-
"madeAt": 1771567906111,
15+
"madeAt": 1771567910429,
3116
"expiresAt": 1774159894900
3217
},
33-
"1113214|eslint-config-prettier>eslint>@eslint-community/eslint-utils>ajv": {
18+
"1113296|fixturify>matcher-collection>minimatch": {
3419
"decision": "ignore",
35-
"madeAt": 1771567906111,
20+
"madeAt": 1771567910429,
3621
"expiresAt": 1774159894900
3722
},
38-
"1113214|eslint-plugin-prettier>eslint>@eslint-community/eslint-utils>ajv": {
23+
"1113296|mocha>glob>minimatch": {
3924
"decision": "ignore",
40-
"madeAt": 1771567906111,
25+
"madeAt": 1771567910429,
4126
"expiresAt": 1774159894900
4227
},
43-
"1113214|eslint-plugin-unicorn>@eslint-community/eslint-utils>eslint>ajv": {
28+
"1113296|nyc>glob>minimatch": {
4429
"decision": "ignore",
45-
"madeAt": 1771567906111,
30+
"madeAt": 1771567910429,
4631
"expiresAt": 1774159894900
4732
},
48-
"1113214|eslint-plugin-vue>@eslint-community/eslint-utils>eslint>ajv": {
33+
"1113296|typescript-eslint>@typescript-eslint/eslint-plugin>@typescript-eslint/parser>@typescript-eslint/typescript-estree>minimatch": {
4934
"decision": "ignore",
50-
"madeAt": 1771567906111,
35+
"madeAt": 1771567910429,
5136
"expiresAt": 1774159894900
5237
},
53-
"1113214|typescript-eslint>@typescript-eslint/eslint-plugin>@typescript-eslint/parser>@typescript-eslint/typescript-estree>minimatch>eslint>@eslint-community/eslint-utils>ajv": {
38+
"1113296|wasm-pack>binary-install>rimraf>glob>minimatch": {
5439
"decision": "ignore",
55-
"madeAt": 1771567906111,
40+
"madeAt": 1771567910429,
5641
"expiresAt": 1774159894900
5742
},
58-
"1113214|vue-eslint-parser>eslint>@eslint-community/eslint-utils>ajv": {
43+
"1113371|@typescript-eslint/eslint-plugin>@typescript-eslint/parser>@typescript-eslint/typescript-estree>minimatch": {
5944
"decision": "ignore",
60-
"madeAt": 1771567906111,
61-
"expiresAt": 1774159894900
45+
"madeAt": 1771655968819,
46+
"expiresAt": 1774247954442
6247
},
63-
"1113296|@typescript-eslint/eslint-plugin>@typescript-eslint/parser>@typescript-eslint/typescript-estree>minimatch": {
48+
"1113371|eslint-plugin-vue>@typescript-eslint/parser>@typescript-eslint/typescript-estree>minimatch": {
6449
"decision": "ignore",
65-
"madeAt": 1771567910429,
66-
"expiresAt": 1774159894900
50+
"madeAt": 1771656226170,
51+
"expiresAt": 1774248219706
6752
},
68-
"1113296|@typescript-eslint/type-utils>@typescript-eslint/typescript-estree>minimatch": {
53+
"1113371|fixturify>matcher-collection>minimatch": {
6954
"decision": "ignore",
70-
"madeAt": 1771567910429,
71-
"expiresAt": 1774159894900
55+
"madeAt": 1771656226170,
56+
"expiresAt": 1774248219706
7257
},
73-
"1113296|eslint-plugin-vue>@eslint-community/eslint-utils>eslint>ajv>@typescript-eslint/parser>@typescript-eslint/typescript-estree>minimatch": {
58+
"1113371|mocha>glob>minimatch": {
7459
"decision": "ignore",
75-
"madeAt": 1771567910429,
76-
"expiresAt": 1774159894900
60+
"madeAt": 1771656226170,
61+
"expiresAt": 1774248219706
7762
},
78-
"1113296|fixturify>matcher-collection>minimatch": {
63+
"1113371|nyc>glob>minimatch": {
7964
"decision": "ignore",
80-
"madeAt": 1771567910429,
81-
"expiresAt": 1774159894900
65+
"madeAt": 1771656226170,
66+
"expiresAt": 1774248219706
8267
},
83-
"1113296|mocha>glob>minimatch": {
68+
"1113371|typescript-eslint>@typescript-eslint/eslint-plugin>@typescript-eslint/parser>@typescript-eslint/typescript-estree>minimatch": {
8469
"decision": "ignore",
85-
"madeAt": 1771567910429,
86-
"expiresAt": 1774159894900
70+
"madeAt": 1771656226170,
71+
"expiresAt": 1774248219706
8772
},
88-
"1113296|nyc>glob>minimatch": {
73+
"1113371|wasm-pack>binary-install>rimraf>glob>minimatch": {
8974
"decision": "ignore",
90-
"madeAt": 1771567910429,
91-
"expiresAt": 1774159894900
75+
"madeAt": 1771656226170,
76+
"expiresAt": 1774248219706
9277
},
93-
"1113296|typescript-eslint>@typescript-eslint/eslint-plugin>@typescript-eslint/parser>@typescript-eslint/typescript-estree>minimatch": {
78+
"1113398|ajv": {
9479
"decision": "ignore",
95-
"madeAt": 1771567910429,
96-
"expiresAt": 1774159894900
80+
"madeAt": 1771656216790,
81+
"expiresAt": 1774248208896
9782
},
98-
"1113296|wasm-pack>binary-install>rimraf>glob>minimatch": {
83+
"1113371|@typescript-eslint/type-utils>@typescript-eslint/typescript-estree>minimatch": {
9984
"decision": "ignore",
100-
"madeAt": 1771567910429,
101-
"expiresAt": 1774159894900
85+
"madeAt": 1771656226170,
86+
"expiresAt": 1774248219706
10287
}
10388
},
10489
"rules": {},

‎browser/src/path.ts‎

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,31 @@ export function extname(path: string): string {
3535
return match ? match[0] : '';
3636
}
3737

38+
export function join(...segments: string[]): string {
39+
const joined = segments.join('/');
40+
const absolute = ANY_SLASH_REGEX.test(joined[0]);
41+
return (
42+
(absolute ? '/' : '') +
43+
(normalizePathSegments(joined.split(ANY_SLASH_REGEX), absolute) || (absolute ? '' : '.'))
44+
);
45+
}
46+
47+
function normalizePathSegments(parts: string[], absolute = false): string {
48+
const normalized: string[] = [];
49+
for (const part of parts) {
50+
if (part === '..') {
51+
if (normalized.length > 0 && normalized[normalized.length - 1] !== '..') {
52+
normalized.pop();
53+
} else if (!absolute) {
54+
normalized.push('..');
55+
}
56+
} else if (part !== '.' && part !== '') {
57+
normalized.push(part);
58+
}
59+
}
60+
return normalized.join('/');
61+
}
62+
3863
export function relative(from: string, to: string): string {
3964
const fromParts = from.split(ANY_SLASH_REGEX).filter(Boolean);
4065
const toParts = to.split(ANY_SLASH_REGEX).filter(Boolean);
@@ -60,30 +85,21 @@ export function relative(from: string, to: string): string {
6085
}
6186

6287
export function resolve(...paths: string[]): string {
63-
const firstPathSegment = paths.shift();
64-
if (!firstPathSegment) {
65-
return '/';
66-
}
67-
let resolvedParts = firstPathSegment.split(ANY_SLASH_REGEX);
68-
88+
let parts: string[] = [];
89+
let isAbsoluteResult = false;
6990
for (const path of paths) {
7091
if (isAbsolute(path)) {
71-
resolvedParts = path.split(ANY_SLASH_REGEX);
92+
parts = path.split(ANY_SLASH_REGEX);
93+
isAbsoluteResult = true;
7294
} else {
73-
const parts = path.split(ANY_SLASH_REGEX);
74-
75-
while (parts[0] === '.' || parts[0] === '..') {
76-
const part = parts.shift();
77-
if (part === '..') {
78-
resolvedParts.pop();
79-
}
80-
}
81-
82-
resolvedParts.push(...parts);
95+
parts.push(...path.split(ANY_SLASH_REGEX));
8396
}
8497
}
85-
86-
return resolvedParts.join('/');
98+
const normalized = normalizePathSegments(parts, isAbsoluteResult);
99+
if (!isAbsoluteResult) return normalized || '/';
100+
// Windows absolute paths (e.g. "C:/path") must not get a leading "/" prepended.
101+
// Unix absolute paths must start with "/".
102+
return /^[A-Za-z]:/.test(normalized) ? normalized : '/' + normalized;
87103
}
88104

89105
// Used for running the browser build locally in Vite

0 commit comments

Comments
 (0)