fix(core): migrate old invalid glob syntax in target outputs (#18191)
This commit is contained in:
parent
c4e56858b0
commit
fef5b65b45
@ -313,13 +313,13 @@ Sometimes, multiple targets might write to the same directory. When possible it
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
But if the above is not possible, globs (parsed with the [minimatch](https://github.com/isaacs/minimatch) library) can be specified as outputs to only cache a set of files rather than the whole directory.
|
But if the above is not possible, globs (parsed by the [GlobSet](https://docs.rs/globset/0.4.5/globset/#syntax) Rust library) can be specified as outputs to only cache a set of files rather than the whole directory.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"targets": {
|
"targets": {
|
||||||
"build-js": {
|
"build-js": {
|
||||||
"outputs": ["{workspaceRoot}/dist/libs/mylib/**/*.js"]
|
"outputs": ["{workspaceRoot}/dist/libs/mylib/**/*.{js,map}"]
|
||||||
},
|
},
|
||||||
"build-css": {
|
"build-css": {
|
||||||
"outputs": ["{workspaceRoot}/dist/libs/mylib/**/*.css"]
|
"outputs": ["{workspaceRoot}/dist/libs/mylib/**/*.css"]
|
||||||
|
|||||||
@ -157,7 +157,7 @@ describe('cache', () => {
|
|||||||
updateProjectConfig(mylib, (c) => {
|
updateProjectConfig(mylib, (c) => {
|
||||||
c.targets.build = {
|
c.targets.build = {
|
||||||
executor: 'nx:run-commands',
|
executor: 'nx:run-commands',
|
||||||
outputs: ['{workspaceRoot}/dist/*.txt'],
|
outputs: ['{workspaceRoot}/dist/*.{txt,md}'],
|
||||||
options: {
|
options: {
|
||||||
commands: [
|
commands: [
|
||||||
'rm -rf dist',
|
'rm -rf dist',
|
||||||
@ -167,7 +167,8 @@ describe('cache', () => {
|
|||||||
'echo c > dist/c.txt',
|
'echo c > dist/c.txt',
|
||||||
'echo d > dist/d.txt',
|
'echo d > dist/d.txt',
|
||||||
'echo e > dist/e.txt',
|
'echo e > dist/e.txt',
|
||||||
'echo f > dist/f.txt',
|
'echo f > dist/f.md',
|
||||||
|
'echo g > dist/g.html',
|
||||||
],
|
],
|
||||||
parallel: false,
|
parallel: false,
|
||||||
},
|
},
|
||||||
@ -188,7 +189,8 @@ describe('cache', () => {
|
|||||||
expect(outputsWithUntouchedOutputs).toContain('c.txt');
|
expect(outputsWithUntouchedOutputs).toContain('c.txt');
|
||||||
expect(outputsWithUntouchedOutputs).toContain('d.txt');
|
expect(outputsWithUntouchedOutputs).toContain('d.txt');
|
||||||
expect(outputsWithUntouchedOutputs).toContain('e.txt');
|
expect(outputsWithUntouchedOutputs).toContain('e.txt');
|
||||||
expect(outputsWithUntouchedOutputs).toContain('f.txt');
|
expect(outputsWithUntouchedOutputs).toContain('f.md');
|
||||||
|
expect(outputsWithUntouchedOutputs).toContain('g.html');
|
||||||
|
|
||||||
// Create a file in the dist that does not match output glob
|
// Create a file in the dist that does not match output glob
|
||||||
updateFile('dist/c.ts', '');
|
updateFile('dist/c.ts', '');
|
||||||
@ -202,7 +204,8 @@ describe('cache', () => {
|
|||||||
expect(outputsAfterAddingUntouchedFileAndRerunning).toContain('c.txt');
|
expect(outputsAfterAddingUntouchedFileAndRerunning).toContain('c.txt');
|
||||||
expect(outputsAfterAddingUntouchedFileAndRerunning).toContain('d.txt');
|
expect(outputsAfterAddingUntouchedFileAndRerunning).toContain('d.txt');
|
||||||
expect(outputsAfterAddingUntouchedFileAndRerunning).toContain('e.txt');
|
expect(outputsAfterAddingUntouchedFileAndRerunning).toContain('e.txt');
|
||||||
expect(outputsAfterAddingUntouchedFileAndRerunning).toContain('f.txt');
|
expect(outputsAfterAddingUntouchedFileAndRerunning).toContain('f.md');
|
||||||
|
expect(outputsAfterAddingUntouchedFileAndRerunning).toContain('g.html');
|
||||||
expect(outputsAfterAddingUntouchedFileAndRerunning).toContain('c.ts');
|
expect(outputsAfterAddingUntouchedFileAndRerunning).toContain('c.ts');
|
||||||
|
|
||||||
// Clear Dist
|
// Clear Dist
|
||||||
@ -217,8 +220,9 @@ describe('cache', () => {
|
|||||||
expect(outputsWithoutOutputs).toContain('c.txt');
|
expect(outputsWithoutOutputs).toContain('c.txt');
|
||||||
expect(outputsWithoutOutputs).toContain('d.txt');
|
expect(outputsWithoutOutputs).toContain('d.txt');
|
||||||
expect(outputsWithoutOutputs).toContain('e.txt');
|
expect(outputsWithoutOutputs).toContain('e.txt');
|
||||||
expect(outputsWithoutOutputs).toContain('f.txt');
|
expect(outputsWithoutOutputs).toContain('f.md');
|
||||||
expect(outputsWithoutOutputs).not.toContain('c.ts');
|
expect(outputsWithoutOutputs).not.toContain('c.ts');
|
||||||
|
expect(outputsWithoutOutputs).not.toContain('g.html');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use consider filesets when hashing', async () => {
|
it('should use consider filesets when hashing', async () => {
|
||||||
|
|||||||
@ -77,6 +77,12 @@
|
|||||||
"version": "16.2.0-beta.0",
|
"version": "16.2.0-beta.0",
|
||||||
"description": "Remove outputPath from run commands",
|
"description": "Remove outputPath from run commands",
|
||||||
"implementation": "./src/migrations/update-16-2-0/remove-run-commands-output-path"
|
"implementation": "./src/migrations/update-16-2-0/remove-run-commands-output-path"
|
||||||
|
},
|
||||||
|
"16.5.4-update-output-globs": {
|
||||||
|
"cli": "nx",
|
||||||
|
"version": "16.5.4-beta.0",
|
||||||
|
"description": "Update outdated non-standard globs to unix standard",
|
||||||
|
"implementation": "./src/migrations/update-16-5-4/update-output-globs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,77 @@
|
|||||||
|
import { createTreeWithEmptyWorkspace } from '../../generators/testing-utils/create-tree-with-empty-workspace';
|
||||||
|
import { TargetConfiguration } from '../../config/workspace-json-project-json';
|
||||||
|
import {
|
||||||
|
addProjectConfiguration,
|
||||||
|
readProjectConfiguration,
|
||||||
|
} from '../../generators/utils/project-configuration';
|
||||||
|
import updateOutputsGlobs from './update-output-globs';
|
||||||
|
import { readJson, updateJson } from '../../generators/utils/json';
|
||||||
|
import { NxJsonConfiguration } from '../../config/nx-json';
|
||||||
|
|
||||||
|
describe('update output globs', () => {
|
||||||
|
it('should update output globs', () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
const targets: Record<string, TargetConfiguration> = {
|
||||||
|
build: {
|
||||||
|
outputs: ['{options.outputPath}', 'dist/apps/my-app/*.(js|map|ts)'],
|
||||||
|
},
|
||||||
|
lint: {},
|
||||||
|
test: {
|
||||||
|
outputs: ['dist/apps/my-app/main.(js|map|ts)'],
|
||||||
|
},
|
||||||
|
run: {
|
||||||
|
outputs: ['dist/apps/my-app'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
addProjectConfiguration(tree, 'my-app', {
|
||||||
|
root: 'apps/my-app',
|
||||||
|
targets,
|
||||||
|
});
|
||||||
|
|
||||||
|
updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
|
||||||
|
json.targetDefaults = {
|
||||||
|
lint: {
|
||||||
|
outputs: ['dist/apps/my-app', '*.(js|map|ts)'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
updateOutputsGlobs(tree);
|
||||||
|
|
||||||
|
const migratedTargets = readProjectConfiguration(tree, 'my-app').targets;
|
||||||
|
expect(migratedTargets).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"outputs": [
|
||||||
|
"{options.outputPath}",
|
||||||
|
"dist/apps/my-app/*.{js,map,ts}",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"lint": {},
|
||||||
|
"run": {
|
||||||
|
"outputs": [
|
||||||
|
"dist/apps/my-app",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"outputs": [
|
||||||
|
"dist/apps/my-app/main.{js,map,ts}",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const nxJson = readJson<NxJsonConfiguration>(tree, 'nx.json');
|
||||||
|
expect(nxJson.targetDefaults).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"lint": {
|
||||||
|
"outputs": [
|
||||||
|
"dist/apps/my-app",
|
||||||
|
"*.{js,map,ts}",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
import { Tree } from '../../generators/tree';
|
||||||
|
import {
|
||||||
|
getProjects,
|
||||||
|
updateProjectConfiguration,
|
||||||
|
} from '../../generators/utils/project-configuration';
|
||||||
|
import { formatChangedFilesWithPrettierIfAvailable } from '../../generators/internal-utils/format-changed-files-with-prettier-if-available';
|
||||||
|
import { TargetConfiguration } from '../../config/workspace-json-project-json';
|
||||||
|
import { updateJson } from '../../generators/utils/json';
|
||||||
|
import { NxJsonConfiguration } from '../../config/nx-json';
|
||||||
|
|
||||||
|
function replaceOutput(output: string) {
|
||||||
|
// replace {projectRoot}/folder/*.(js|map|ts) to {projectRoot}/folder/*.{js,map,ts}
|
||||||
|
const regex = /\(([^)]+)\)/g;
|
||||||
|
return output.replace(regex, (match, group1) => {
|
||||||
|
let replacements = group1.split('|').join(',');
|
||||||
|
return `{${replacements}}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function updateOutputsGlobs(tree: Tree) {
|
||||||
|
for (const [projectName, projectConfiguration] of getProjects(
|
||||||
|
tree
|
||||||
|
).entries()) {
|
||||||
|
for (const [targetName, targetConfiguration] of Object.entries(
|
||||||
|
projectConfiguration.targets ?? {}
|
||||||
|
)) {
|
||||||
|
if (!Array.isArray(targetConfiguration.outputs)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetConfiguration.outputs =
|
||||||
|
targetConfiguration.outputs.map(replaceOutput);
|
||||||
|
}
|
||||||
|
updateProjectConfiguration(tree, projectName, projectConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tree.exists('nx.json')) {
|
||||||
|
updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
|
||||||
|
for (const [, targetConfiguration] of Object.entries(
|
||||||
|
json.targetDefaults ?? {}
|
||||||
|
)) {
|
||||||
|
if (!Array.isArray(targetConfiguration.outputs)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetConfiguration.outputs =
|
||||||
|
targetConfiguration.outputs.map(replaceOutput);
|
||||||
|
}
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await formatChangedFilesWithPrettierIfAvailable(tree);
|
||||||
|
}
|
||||||
16
packages/nx/src/native/cache/expand_outputs.rs
vendored
16
packages/nx/src/native/cache/expand_outputs.rs
vendored
@ -59,6 +59,10 @@ mod test {
|
|||||||
.child("nx.darwin-arm64.node")
|
.child("nx.darwin-arm64.node")
|
||||||
.touch()
|
.touch()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
temp.child("multi").child("file.js").touch().unwrap();
|
||||||
|
temp.child("multi").child("src.ts").touch().unwrap();
|
||||||
|
temp.child("multi").child("file.map").touch().unwrap();
|
||||||
|
temp.child("multi").child("file.txt").touch().unwrap();
|
||||||
temp
|
temp
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@ -80,4 +84,16 @@ mod test {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_handle_multiple_extensions() {
|
||||||
|
let temp = setup_fs();
|
||||||
|
let entries = vec!["multi/*.{js,map,ts}".to_string()];
|
||||||
|
let mut result = expand_outputs(temp.display().to_string(), entries).unwrap();
|
||||||
|
result.sort();
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
vec!["multi/file.js", "multi/file.map", "multi/src.ts"]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -182,7 +182,10 @@ export function getOutputsForTargetAndConfiguration(
|
|||||||
options,
|
options,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.filter((output) => !!output && !output.match(/{.*}/));
|
.filter(
|
||||||
|
(output) =>
|
||||||
|
!!output && !output.match(/{(projectRoot|workspaceRoot|(options.*))}/)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep backwards compatibility in case `outputs` doesn't exist
|
// Keep backwards compatibility in case `outputs` doesn't exist
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user