feat(vite): add convert-to-inferred migration generator (#26249)
- feat(vite): add convert-to-inferred generator for migrating to inference - feat(vite): add build postTargetTransformer - feat(vite): add serve, preview, test postTargetTransformer - feat(vite): convert-to-inferred should clean up inputs and outputs - docs(vite): add convert-to-inferred - feat(vite): update outDir correctly <!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> ## Current Behavior <!-- This is the behavior we have today --> There is currently no generator that can migrate projects that use `@nx/vite:*` executors to use Inference plugins. ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> Add `@nx/vite:convert-to-inferred` generator ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes # --------- Co-authored-by: Jack Hsu <jack.hsu@gmail.com>
This commit is contained in:
parent
e9b7439ce2
commit
b36c39e331
@ -9593,6 +9593,14 @@
|
||||
"isExternal": false,
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"id": "convert-to-inferred",
|
||||
"path": "/nx-api/vite/generators/convert-to-inferred",
|
||||
"name": "convert-to-inferred",
|
||||
"children": [],
|
||||
"isExternal": false,
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"id": "vitest",
|
||||
"path": "/nx-api/vite/generators/vitest",
|
||||
|
||||
@ -2968,6 +2968,15 @@
|
||||
"path": "/nx-api/vite/generators/setup-paths-plugin",
|
||||
"type": "generator"
|
||||
},
|
||||
"/nx-api/vite/generators/convert-to-inferred": {
|
||||
"description": "Convert existing Vite project(s) using `@nx/vite:*` executors to use `@nx/vite/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target.",
|
||||
"file": "generated/packages/vite/generators/convert-to-inferred.json",
|
||||
"hidden": false,
|
||||
"name": "convert-to-inferred",
|
||||
"originalFilePath": "/packages/vite/src/generators/convert-to-inferred/schema.json",
|
||||
"path": "/nx-api/vite/generators/convert-to-inferred",
|
||||
"type": "generator"
|
||||
},
|
||||
"/nx-api/vite/generators/vitest": {
|
||||
"description": "Generate a vitest configuration.",
|
||||
"file": "generated/packages/vite/generators/vitest.json",
|
||||
|
||||
@ -2936,6 +2936,15 @@
|
||||
"path": "vite/generators/setup-paths-plugin",
|
||||
"type": "generator"
|
||||
},
|
||||
{
|
||||
"description": "Convert existing Vite project(s) using `@nx/vite:*` executors to use `@nx/vite/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target.",
|
||||
"file": "generated/packages/vite/generators/convert-to-inferred.json",
|
||||
"hidden": false,
|
||||
"name": "convert-to-inferred",
|
||||
"originalFilePath": "/packages/vite/src/generators/convert-to-inferred/schema.json",
|
||||
"path": "vite/generators/convert-to-inferred",
|
||||
"type": "generator"
|
||||
},
|
||||
{
|
||||
"description": "Generate a vitest configuration.",
|
||||
"file": "generated/packages/vite/generators/vitest.json",
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "convert-to-inferred",
|
||||
"factory": "./src/generators/convert-to-inferred/convert-to-inferred",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/schema",
|
||||
"$id": "NxViteConvertToInferred",
|
||||
"description": "Convert existing Vite project(s) using `@nx/vite:*` executors to use `@nx/vite/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target.",
|
||||
"title": "Convert Vite project from executor to plugin",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"project": {
|
||||
"type": "string",
|
||||
"description": "The project to convert from using the `@nx/vite:*` executors to use `@nx/vite/plugin`.",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"skipFormat": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to format files at the end of the migration.",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"presets": []
|
||||
},
|
||||
"description": "Convert existing Vite project(s) using `@nx/vite:*` executors to use `@nx/vite/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target.",
|
||||
"implementation": "/packages/vite/src/generators/convert-to-inferred/convert-to-inferred.ts",
|
||||
"aliases": [],
|
||||
"hidden": false,
|
||||
"path": "/packages/vite/src/generators/convert-to-inferred/schema.json",
|
||||
"type": "generator"
|
||||
}
|
||||
@ -676,6 +676,7 @@
|
||||
- [init](/nx-api/vite/generators/init)
|
||||
- [configuration](/nx-api/vite/generators/configuration)
|
||||
- [setup-paths-plugin](/nx-api/vite/generators/setup-paths-plugin)
|
||||
- [convert-to-inferred](/nx-api/vite/generators/convert-to-inferred)
|
||||
- [vitest](/nx-api/vite/generators/vitest)
|
||||
- [vue](/nx-api/vue)
|
||||
- [documents](/nx-api/vue/documents)
|
||||
|
||||
@ -21,6 +21,11 @@
|
||||
"schema": "./src/generators/setup-paths-plugin/schema.json",
|
||||
"description": "Sets up the nxViteTsPaths plugin to enable support for workspace libraries."
|
||||
},
|
||||
"convert-to-inferred": {
|
||||
"factory": "./src/generators/convert-to-inferred/convert-to-inferred",
|
||||
"schema": "./src/generators/convert-to-inferred/schema.json",
|
||||
"description": "Convert existing Vite project(s) using `@nx/vite:*` executors to use `@nx/vite/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target."
|
||||
},
|
||||
"vitest": {
|
||||
"factory": "./src/generators/vitest/vitest-generator#vitestGeneratorInternal",
|
||||
"schema": "./src/generators/vitest/schema.json",
|
||||
|
||||
@ -0,0 +1,634 @@
|
||||
import {
|
||||
getRelativeProjectJsonSchemaPath,
|
||||
updateProjectConfiguration,
|
||||
} from 'nx/src/generators/utils/project-configuration';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { convertToInferred } from './convert-to-inferred';
|
||||
import {
|
||||
addProjectConfiguration as _addProjectConfiguration,
|
||||
type ExpandedPluginConfiguration,
|
||||
joinPathFragments,
|
||||
type ProjectConfiguration,
|
||||
type ProjectGraph,
|
||||
readNxJson,
|
||||
readProjectConfiguration,
|
||||
type Tree,
|
||||
updateNxJson,
|
||||
writeJson,
|
||||
} from '@nx/devkit';
|
||||
import { TempFs } from '@nx/devkit/internal-testing-utils';
|
||||
import { join } from 'node:path';
|
||||
|
||||
let fs: TempFs;
|
||||
|
||||
let projectGraph: ProjectGraph;
|
||||
|
||||
let mockedConfigs = {};
|
||||
const getMockedConfig = (
|
||||
opts: { configFile: string; mode: 'development' },
|
||||
target: string
|
||||
) => {
|
||||
const relativeConfigFile = opts.configFile.replace(`${fs.tempDir}/`, '');
|
||||
return Promise.resolve({
|
||||
path: opts.configFile,
|
||||
config: mockedConfigs[relativeConfigFile],
|
||||
build: mockedConfigs[relativeConfigFile]['build'],
|
||||
test: mockedConfigs[relativeConfigFile]['test'],
|
||||
dependencies: [],
|
||||
});
|
||||
};
|
||||
|
||||
jest.mock('vite', () => ({
|
||||
resolveConfig: jest.fn().mockImplementation(getMockedConfig),
|
||||
}));
|
||||
|
||||
jest.mock('../../utils/executor-utils', () => ({
|
||||
loadViteDynamicImport: jest.fn().mockImplementation(() => ({
|
||||
resolveConfig: jest.fn().mockImplementation(getMockedConfig),
|
||||
})),
|
||||
}));
|
||||
|
||||
jest.mock('@nx/devkit', () => ({
|
||||
...jest.requireActual<any>('@nx/devkit'),
|
||||
createProjectGraphAsync: jest.fn().mockImplementation(async () => {
|
||||
return projectGraph;
|
||||
}),
|
||||
updateProjectConfiguration: jest
|
||||
.fn()
|
||||
.mockImplementation((tree, projectName, projectConfiguration) => {
|
||||
function handleEmptyTargets(
|
||||
projectName: string,
|
||||
projectConfiguration: ProjectConfiguration
|
||||
): void {
|
||||
if (
|
||||
projectConfiguration.targets &&
|
||||
!Object.keys(projectConfiguration.targets).length
|
||||
) {
|
||||
// Re-order `targets` to appear after the `// target` comment.
|
||||
delete projectConfiguration.targets;
|
||||
projectConfiguration[
|
||||
'// targets'
|
||||
] = `to see all targets run: nx show project ${projectName} --web`;
|
||||
projectConfiguration.targets = {};
|
||||
} else {
|
||||
delete projectConfiguration['// targets'];
|
||||
}
|
||||
}
|
||||
|
||||
const projectConfigFile = joinPathFragments(
|
||||
projectConfiguration.root,
|
||||
'project.json'
|
||||
);
|
||||
|
||||
if (!tree.exists(projectConfigFile)) {
|
||||
throw new Error(
|
||||
`Cannot update Project ${projectName} at ${projectConfiguration.root}. It either doesn't exist yet, or may not use project.json for configuration. Use \`addProjectConfiguration()\` instead if you want to create a new project.`
|
||||
);
|
||||
}
|
||||
handleEmptyTargets(projectName, projectConfiguration);
|
||||
writeJson(tree, projectConfigFile, {
|
||||
name: projectConfiguration.name ?? projectName,
|
||||
$schema: getRelativeProjectJsonSchemaPath(tree, projectConfiguration),
|
||||
...projectConfiguration,
|
||||
root: undefined,
|
||||
});
|
||||
projectGraph.nodes[projectName].data = projectConfiguration;
|
||||
}),
|
||||
}));
|
||||
|
||||
function addProjectConfiguration(
|
||||
tree: Tree,
|
||||
name: string,
|
||||
project: ProjectConfiguration
|
||||
) {
|
||||
_addProjectConfiguration(tree, name, project);
|
||||
projectGraph.nodes[name] = {
|
||||
name: name,
|
||||
type: project.projectType === 'application' ? 'app' : 'lib',
|
||||
data: {
|
||||
projectType: project.projectType,
|
||||
root: project.root,
|
||||
targets: project.targets,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
interface CreateViteTestProjectOptions {
|
||||
appName: string;
|
||||
appRoot: string;
|
||||
buildTargetName: string;
|
||||
serveTargetName: string;
|
||||
previewTargetName: string;
|
||||
testTargetName: string;
|
||||
outputPath: string;
|
||||
}
|
||||
|
||||
const defaultCreateViteTestProjectOptions: CreateViteTestProjectOptions = {
|
||||
appName: 'myapp',
|
||||
appRoot: 'myapp',
|
||||
buildTargetName: 'build',
|
||||
serveTargetName: 'serve',
|
||||
previewTargetName: 'preview',
|
||||
testTargetName: 'test',
|
||||
outputPath: '{workspaceRoot}/dist/myapp',
|
||||
};
|
||||
|
||||
function createTestProject(
|
||||
tree: Tree,
|
||||
opts: Partial<CreateViteTestProjectOptions> = defaultCreateViteTestProjectOptions
|
||||
) {
|
||||
let projectOpts = { ...defaultCreateViteTestProjectOptions, ...opts };
|
||||
const project: ProjectConfiguration = {
|
||||
name: projectOpts.appName,
|
||||
root: projectOpts.appRoot,
|
||||
projectType: 'application',
|
||||
targets: {
|
||||
[projectOpts.buildTargetName]: {
|
||||
executor: '@nx/vite:build',
|
||||
outputs: [projectOpts.outputPath],
|
||||
options: {
|
||||
configFile: `${projectOpts.appRoot}/vite.config.ts`,
|
||||
},
|
||||
},
|
||||
[projectOpts.serveTargetName]: {
|
||||
executor: '@nx/vite:dev-server',
|
||||
options: {
|
||||
buildTarget: `${projectOpts.appName}:${projectOpts.buildTargetName}`,
|
||||
},
|
||||
},
|
||||
[projectOpts.previewTargetName]: {
|
||||
executor: '@nx/vite:preview-server',
|
||||
options: {
|
||||
buildTarget: `${projectOpts.appName}:${projectOpts.buildTargetName}`,
|
||||
},
|
||||
},
|
||||
[projectOpts.testTargetName]: {
|
||||
executor: '@nx/vite:test',
|
||||
options: {
|
||||
configFile: `${projectOpts.appRoot}/vite.config.ts`,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const viteConfigContents = `/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
|
||||
|
||||
export default defineConfig({
|
||||
root: __dirname,
|
||||
cacheDir: '../../node_modules/.vite/apps/myapp',
|
||||
|
||||
server: {
|
||||
port: 4200,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
preview: {
|
||||
port: 4300,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
plugins: [react(), nxViteTsPaths()],
|
||||
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
|
||||
build: {
|
||||
outDir: '../../dist/apps/myapp',
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
},
|
||||
|
||||
test: {
|
||||
globals: true,
|
||||
cache: {
|
||||
dir: '../../node_modules/.vitest',
|
||||
},
|
||||
environment: 'jsdom',
|
||||
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: '../../coverage/apps/myapp',
|
||||
provider: 'v8',
|
||||
},
|
||||
},
|
||||
});`;
|
||||
|
||||
tree.write(`${projectOpts.appRoot}/vite.config.ts`, viteConfigContents);
|
||||
fs.createFileSync(
|
||||
`${projectOpts.appRoot}/vite.config.ts`,
|
||||
viteConfigContents
|
||||
);
|
||||
tree.write(`${projectOpts.appRoot}/index.html`, `<html></html>`);
|
||||
fs.createFileSync(`${projectOpts.appRoot}/index.html`, `<html></html>`);
|
||||
|
||||
mockedConfigs[`${projectOpts.appRoot}/vite.config.ts`] = {
|
||||
root: `${projectOpts.appRoot}`,
|
||||
cacheDir: `../../node_modules/.vite/${projectOpts.appName}`,
|
||||
|
||||
server: {
|
||||
port: 4200,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
preview: {
|
||||
port: 4300,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
build: {
|
||||
outDir: `../../dist/${projectOpts.appRoot}`,
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
},
|
||||
|
||||
test: {
|
||||
globals: true,
|
||||
cache: {
|
||||
dir: '../../node_modules/.vitest',
|
||||
},
|
||||
environment: 'jsdom',
|
||||
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: `../../coverage/${projectOpts.appRoot}`,
|
||||
provider: 'v8',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
jest.doMock(
|
||||
join(fs.tempDir, `${projectOpts.appRoot}/vite.config.ts`),
|
||||
() => ({
|
||||
default: mockedConfigs[`${projectOpts.appRoot}/vite.config.ts`],
|
||||
}),
|
||||
{
|
||||
virtual: true,
|
||||
}
|
||||
);
|
||||
|
||||
addProjectConfiguration(tree, project.name, project);
|
||||
fs.createFileSync(
|
||||
`${projectOpts.appRoot}/project.json`,
|
||||
JSON.stringify(project)
|
||||
);
|
||||
return project;
|
||||
}
|
||||
|
||||
describe('Vite - Convert Executors To Plugin', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
fs = new TempFs('vite');
|
||||
tree = createTreeWithEmptyWorkspace();
|
||||
tree.root = fs.tempDir;
|
||||
mockedConfigs = {};
|
||||
|
||||
projectGraph = {
|
||||
nodes: {},
|
||||
dependencies: {},
|
||||
externalNodes: {},
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.reset();
|
||||
});
|
||||
|
||||
describe('--project', () => {
|
||||
it('should setup a new Vite plugin and only migrate one specific project', async () => {
|
||||
// ARRANGE
|
||||
const existingProject = createTestProject(tree, {
|
||||
appRoot: 'existing',
|
||||
appName: 'existing',
|
||||
buildTargetName: 'build',
|
||||
});
|
||||
const project = createTestProject(tree, {
|
||||
buildTargetName: 'build-base',
|
||||
});
|
||||
const secondProject = createTestProject(tree, {
|
||||
appRoot: 'second',
|
||||
appName: 'second',
|
||||
buildTargetName: 'build-base',
|
||||
});
|
||||
const thirdProject = createTestProject(tree, {
|
||||
appRoot: 'third',
|
||||
appName: 'third',
|
||||
buildTargetName: 'package',
|
||||
});
|
||||
const nxJson = readNxJson(tree);
|
||||
nxJson.plugins ??= [];
|
||||
nxJson.plugins.push({
|
||||
plugin: '@nx/vite/plugin',
|
||||
options: {
|
||||
buildTargetName: 'build',
|
||||
testTargetName: 'test',
|
||||
previewTargetName: 'preview',
|
||||
serveTargetName: 'serve',
|
||||
},
|
||||
});
|
||||
updateNxJson(tree, nxJson);
|
||||
|
||||
// ACT
|
||||
await convertToInferred(tree, { project: 'myapp', skipFormat: true });
|
||||
|
||||
// ASSERT
|
||||
// project.json modifications
|
||||
const updatedProject = readProjectConfiguration(tree, project.name);
|
||||
expect(updatedProject.targets).toMatchInlineSnapshot(`
|
||||
{
|
||||
"build-base": {
|
||||
"options": {
|
||||
"config": "./vite.config.ts",
|
||||
},
|
||||
},
|
||||
"test": {
|
||||
"options": {
|
||||
"config": "./vite.config.ts",
|
||||
},
|
||||
},
|
||||
}
|
||||
`);
|
||||
|
||||
// nx.json modifications
|
||||
const nxJsonPlugins = readNxJson(tree).plugins;
|
||||
const addedTestVitePlugin = nxJsonPlugins.find((plugin) => {
|
||||
if (
|
||||
typeof plugin !== 'string' &&
|
||||
plugin.plugin === '@nx/vite/plugin' &&
|
||||
plugin.include?.length === 1
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
expect(addedTestVitePlugin).toBeTruthy();
|
||||
expect(
|
||||
(addedTestVitePlugin as ExpandedPluginConfiguration).include
|
||||
).toEqual(['myapp/**/*']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('--all', () => {
|
||||
it('should successfully migrate a project using Vite executors to plugin', async () => {
|
||||
const project = createTestProject(tree);
|
||||
|
||||
// ACT
|
||||
await convertToInferred(tree, { skipFormat: true });
|
||||
|
||||
// ASSERT
|
||||
// project.json modifications
|
||||
const updatedProject = readProjectConfiguration(tree, project.name);
|
||||
const targetKeys = Object.keys(updatedProject.targets);
|
||||
expect(targetKeys).not.toContain('e2e');
|
||||
|
||||
// nx.json modifications
|
||||
const nxJsonPlugins = readNxJson(tree).plugins;
|
||||
const hasVitePlugin = nxJsonPlugins.find((plugin) =>
|
||||
typeof plugin === 'string'
|
||||
? plugin === '@nx/vite/plugin'
|
||||
: plugin.plugin === '@nx/vite/plugin'
|
||||
);
|
||||
expect(hasVitePlugin).toBeTruthy();
|
||||
if (typeof hasVitePlugin !== 'string') {
|
||||
[
|
||||
['buildTargetName', 'build'],
|
||||
['serveTargetName', 'serve'],
|
||||
['previewTargetName', 'preview'],
|
||||
['testTargetName', 'test'],
|
||||
].forEach(([targetOptionName, targetName]) => {
|
||||
expect(hasVitePlugin.options[targetOptionName]).toEqual(targetName);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('should setup Vite plugin to match projects', async () => {
|
||||
// ARRANGE
|
||||
const project = createTestProject(tree, {
|
||||
buildTargetName: 'bundle',
|
||||
});
|
||||
|
||||
// ACT
|
||||
await convertToInferred(tree, { skipFormat: true });
|
||||
|
||||
// ASSERT
|
||||
// project.json modifications
|
||||
const updatedProject = readProjectConfiguration(tree, project.name);
|
||||
expect(updatedProject.targets).toMatchInlineSnapshot(`
|
||||
{
|
||||
"bundle": {
|
||||
"options": {
|
||||
"config": "./vite.config.ts",
|
||||
},
|
||||
},
|
||||
"test": {
|
||||
"options": {
|
||||
"config": "./vite.config.ts",
|
||||
},
|
||||
},
|
||||
}
|
||||
`);
|
||||
|
||||
// nx.json modifications
|
||||
const nxJsonPlugins = readNxJson(tree).plugins;
|
||||
const hasVitePlugin = nxJsonPlugins.find((plugin) =>
|
||||
typeof plugin === 'string'
|
||||
? plugin === '@nx/vite/plugin'
|
||||
: plugin.plugin === '@nx/vite/plugin'
|
||||
);
|
||||
expect(hasVitePlugin).toBeTruthy();
|
||||
if (typeof hasVitePlugin !== 'string') {
|
||||
[
|
||||
['buildTargetName', 'bundle'],
|
||||
['serveTargetName', 'serve'],
|
||||
['previewTargetName', 'preview'],
|
||||
['testTargetName', 'test'],
|
||||
].forEach(([targetOptionName, targetName]) => {
|
||||
expect(hasVitePlugin.options[targetOptionName]).toEqual(targetName);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('should setup a new Vite plugin to match only projects migrated', async () => {
|
||||
// ARRANGE
|
||||
const existingProject = createTestProject(tree, {
|
||||
appRoot: 'existing',
|
||||
appName: 'existing',
|
||||
buildTargetName: 'build',
|
||||
});
|
||||
const project = createTestProject(tree, {
|
||||
buildTargetName: 'bundle',
|
||||
});
|
||||
const secondProject = createTestProject(tree, {
|
||||
appRoot: 'second',
|
||||
appName: 'second',
|
||||
buildTargetName: 'bundle',
|
||||
});
|
||||
const thirdProject = createTestProject(tree, {
|
||||
appRoot: 'third',
|
||||
appName: 'third',
|
||||
buildTargetName: 'build-base',
|
||||
});
|
||||
const nxJson = readNxJson(tree);
|
||||
nxJson.plugins ??= [];
|
||||
nxJson.plugins.push({
|
||||
plugin: '@nx/vite/plugin',
|
||||
options: {
|
||||
buildTargetName: 'build',
|
||||
serveTargetName: 'serve',
|
||||
previewTargetName: 'preview',
|
||||
testTargetName: 'test',
|
||||
},
|
||||
});
|
||||
updateNxJson(tree, nxJson);
|
||||
|
||||
// ACT
|
||||
await convertToInferred(tree, { skipFormat: true });
|
||||
|
||||
// ASSERT
|
||||
// project.json modifications
|
||||
const updatedProject = readProjectConfiguration(tree, project.name);
|
||||
expect(updatedProject.targets).toMatchInlineSnapshot(`
|
||||
{
|
||||
"bundle": {
|
||||
"options": {
|
||||
"config": "./vite.config.ts",
|
||||
},
|
||||
},
|
||||
"test": {
|
||||
"options": {
|
||||
"config": "./vite.config.ts",
|
||||
},
|
||||
},
|
||||
}
|
||||
`);
|
||||
|
||||
// nx.json modifications
|
||||
const nxJsonPlugins = readNxJson(tree).plugins;
|
||||
const addedTestVitePlugin = nxJsonPlugins.find((plugin) => {
|
||||
if (
|
||||
typeof plugin !== 'string' &&
|
||||
plugin.plugin === '@nx/vite/plugin' &&
|
||||
plugin.include?.length === 2
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
expect(addedTestVitePlugin).toBeTruthy();
|
||||
expect(
|
||||
(addedTestVitePlugin as ExpandedPluginConfiguration).include
|
||||
).toEqual(['myapp/**/*', 'second/**/*']);
|
||||
|
||||
const addedIntegrationVitePlugin = nxJsonPlugins.find((plugin) => {
|
||||
if (
|
||||
typeof plugin !== 'string' &&
|
||||
plugin.plugin === '@nx/vite/plugin' &&
|
||||
plugin.include?.length === 1
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
expect(addedIntegrationVitePlugin).toBeTruthy();
|
||||
expect(
|
||||
(addedIntegrationVitePlugin as ExpandedPluginConfiguration).include
|
||||
).toEqual(['third/**/*']);
|
||||
});
|
||||
|
||||
it('should keep Vite options in project.json', async () => {
|
||||
// ARRANGE
|
||||
const project = createTestProject(tree);
|
||||
project.targets.build.options.mode = 'development';
|
||||
updateProjectConfiguration(tree, project.name, project);
|
||||
|
||||
// ACT
|
||||
await convertToInferred(tree, { skipFormat: true });
|
||||
|
||||
// ASSERT
|
||||
// project.json modifications
|
||||
const updatedProject = readProjectConfiguration(tree, project.name);
|
||||
expect(updatedProject.targets.build).toMatchInlineSnapshot(`
|
||||
{
|
||||
"options": {
|
||||
"config": "./vite.config.ts",
|
||||
"mode": "development",
|
||||
},
|
||||
}
|
||||
`);
|
||||
|
||||
// nx.json modifications
|
||||
const nxJsonPlugins = readNxJson(tree).plugins;
|
||||
const hasVitePlugin = nxJsonPlugins.find((plugin) =>
|
||||
typeof plugin === 'string'
|
||||
? plugin === '@nx/vite/plugin'
|
||||
: plugin.plugin === '@nx/vite/plugin'
|
||||
);
|
||||
expect(hasVitePlugin).toBeTruthy();
|
||||
if (typeof hasVitePlugin !== 'string') {
|
||||
[
|
||||
['buildTargetName', 'build'],
|
||||
['serveTargetName', 'serve'],
|
||||
['previewTargetName', 'preview'],
|
||||
['testTargetName', 'test'],
|
||||
].forEach(([targetOptionName, targetName]) => {
|
||||
expect(hasVitePlugin.options[targetOptionName]).toEqual(targetName);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('should add Vite options found in targetDefaults for the executor to the project.json', async () => {
|
||||
// ARRANGE
|
||||
const nxJson = readNxJson(tree);
|
||||
nxJson.targetDefaults ??= {};
|
||||
nxJson.targetDefaults['@nx/vite:build'] = {
|
||||
options: {
|
||||
mode: 'production',
|
||||
},
|
||||
};
|
||||
updateNxJson(tree, nxJson);
|
||||
const project = createTestProject(tree);
|
||||
|
||||
// ACT
|
||||
await convertToInferred(tree, { skipFormat: true });
|
||||
|
||||
// ASSERT
|
||||
// project.json modifications
|
||||
const updatedProject = readProjectConfiguration(tree, project.name);
|
||||
expect(updatedProject.targets.build).toMatchInlineSnapshot(`
|
||||
{
|
||||
"options": {
|
||||
"config": "./vite.config.ts",
|
||||
"mode": "production",
|
||||
},
|
||||
}
|
||||
`);
|
||||
|
||||
// nx.json modifications
|
||||
const nxJsonPlugins = readNxJson(tree).plugins;
|
||||
const hasVitePlugin = nxJsonPlugins.find((plugin) =>
|
||||
typeof plugin === 'string'
|
||||
? plugin === '@nx/vite/plugin'
|
||||
: plugin.plugin === '@nx/vite/plugin'
|
||||
);
|
||||
expect(hasVitePlugin).toBeTruthy();
|
||||
if (typeof hasVitePlugin !== 'string') {
|
||||
[
|
||||
['buildTargetName', 'build'],
|
||||
['serveTargetName', 'serve'],
|
||||
['previewTargetName', 'preview'],
|
||||
['testTargetName', 'test'],
|
||||
].forEach(([targetOptionName, targetName]) => {
|
||||
expect(hasVitePlugin.options[targetOptionName]).toEqual(targetName);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,105 @@
|
||||
import { createProjectGraphAsync, formatFiles, type Tree } from '@nx/devkit';
|
||||
import { migrateExecutorToPlugin } from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator';
|
||||
import { createNodesV2, VitePluginOptions } from '../../plugins/plugin';
|
||||
import { buildPostTargetTransformer } from './lib/build-post-target-transformer';
|
||||
import { servePostTargetTransformer } from './lib/serve-post-target-transformer';
|
||||
import { previewPostTargetTransformer } from './lib/preview-post-target-transformer';
|
||||
import { testPostTargetTransformer } from './lib/test-post-target-transformer';
|
||||
import { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util';
|
||||
|
||||
interface Schema {
|
||||
project?: string;
|
||||
skipFormat?: boolean;
|
||||
}
|
||||
|
||||
export async function convertToInferred(tree: Tree, options: Schema) {
|
||||
const projectGraph = await createProjectGraphAsync();
|
||||
const migrationLogs = new AggregatedLog();
|
||||
const migratedBuildProjects =
|
||||
await migrateExecutorToPlugin<VitePluginOptions>(
|
||||
tree,
|
||||
projectGraph,
|
||||
'@nx/vite:build',
|
||||
'@nx/vite/plugin',
|
||||
(targetName) => ({
|
||||
buildTargetName: targetName,
|
||||
serveTargetName: 'serve',
|
||||
previewTargetName: 'preview',
|
||||
testTargetName: 'test',
|
||||
serveStaticTargetName: 'serve-static',
|
||||
}),
|
||||
buildPostTargetTransformer,
|
||||
createNodesV2,
|
||||
options.project
|
||||
);
|
||||
const migratedServeProjects =
|
||||
await migrateExecutorToPlugin<VitePluginOptions>(
|
||||
tree,
|
||||
projectGraph,
|
||||
'@nx/vite:dev-server',
|
||||
'@nx/vite/plugin',
|
||||
(targetName) => ({
|
||||
buildTargetName: 'build',
|
||||
serveTargetName: targetName,
|
||||
previewTargetName: 'preview',
|
||||
testTargetName: 'test',
|
||||
serveStaticTargetName: 'serve-static',
|
||||
}),
|
||||
servePostTargetTransformer(migrationLogs),
|
||||
createNodesV2,
|
||||
options.project
|
||||
);
|
||||
const migratedPreviewProjects =
|
||||
await migrateExecutorToPlugin<VitePluginOptions>(
|
||||
tree,
|
||||
projectGraph,
|
||||
'@nx/vite:preview-server',
|
||||
'@nx/vite/plugin',
|
||||
(targetName) => ({
|
||||
buildTargetName: 'build',
|
||||
serveTargetName: 'serve',
|
||||
previewTargetName: targetName,
|
||||
testTargetName: 'test',
|
||||
serveStaticTargetName: 'serve-static',
|
||||
}),
|
||||
previewPostTargetTransformer(migrationLogs),
|
||||
createNodesV2,
|
||||
options.project
|
||||
);
|
||||
const migratedTestProjects = await migrateExecutorToPlugin<VitePluginOptions>(
|
||||
tree,
|
||||
projectGraph,
|
||||
'@nx/vite:test',
|
||||
'@nx/vite/plugin',
|
||||
(targetName) => ({
|
||||
buildTargetName: 'build',
|
||||
serveTargetName: 'serve',
|
||||
previewTargetName: 'preview',
|
||||
testTargetName: targetName,
|
||||
serveStaticTargetName: 'serve-static',
|
||||
}),
|
||||
testPostTargetTransformer,
|
||||
createNodesV2,
|
||||
options.project
|
||||
);
|
||||
|
||||
const migratedProjects =
|
||||
migratedBuildProjects.size +
|
||||
migratedServeProjects.size +
|
||||
migratedPreviewProjects.size +
|
||||
migratedTestProjects.size;
|
||||
|
||||
if (migratedProjects === 0) {
|
||||
throw new Error('Could not find any targets to migrate.');
|
||||
}
|
||||
|
||||
if (!options.skipFormat) {
|
||||
await formatFiles(tree);
|
||||
}
|
||||
|
||||
return () => {
|
||||
migrationLogs.flushLogs();
|
||||
};
|
||||
}
|
||||
|
||||
export default convertToInferred;
|
||||
@ -0,0 +1,271 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`buildPostTargetTransformer moveBuildLibsFromSourceToViteConfig should add buildLibsFromSource to existing nxViteTsPaths plugin with existing options 1`] = `
|
||||
"/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
|
||||
|
||||
export default defineConfig({
|
||||
root: __dirname,
|
||||
cacheDir: '../../node_modules/.vite/apps/myapp',
|
||||
|
||||
server: {
|
||||
port: 4200,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
preview: {
|
||||
port: 4300,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
plugins: [react(), nxViteTsPaths({buildLibsFromSource: options.buildLibsFromSource, debug: true })],
|
||||
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
|
||||
build: {
|
||||
outDir: '../../dist/apps/myapp',
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
},
|
||||
|
||||
test: {
|
||||
globals: true,
|
||||
cache: {
|
||||
dir: '../../node_modules/.vitest',
|
||||
},
|
||||
environment: 'jsdom',
|
||||
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: '../../coverage/apps/myapp',
|
||||
provider: 'v8',
|
||||
},
|
||||
},
|
||||
});"
|
||||
`;
|
||||
|
||||
exports[`buildPostTargetTransformer moveBuildLibsFromSourceToViteConfig should add buildLibsFromSource to existing nxViteTsPaths plugin with no existing options 1`] = `
|
||||
"/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
|
||||
|
||||
export default defineConfig({
|
||||
root: __dirname,
|
||||
cacheDir: '../../node_modules/.vite/apps/myapp',
|
||||
|
||||
server: {
|
||||
port: 4200,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
preview: {
|
||||
port: 4300,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
plugins: [react(), nxViteTsPaths({ buildLibsFromSource: options.buildLibsFromSource }),],
|
||||
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
|
||||
build: {
|
||||
outDir: '../../dist/apps/myapp',
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
},
|
||||
|
||||
test: {
|
||||
globals: true,
|
||||
cache: {
|
||||
dir: '../../node_modules/.vitest',
|
||||
},
|
||||
environment: 'jsdom',
|
||||
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: '../../coverage/apps/myapp',
|
||||
provider: 'v8',
|
||||
},
|
||||
},
|
||||
});"
|
||||
`;
|
||||
|
||||
exports[`buildPostTargetTransformer moveBuildLibsFromSourceToViteConfig should add buildLibsFromSource to new nxViteTsPaths plugin when the plugin is not added 1`] = `
|
||||
"import { nxViteTsPaths } from "@nx/vite/plugins/nx-tsconfig-paths.plugin";
|
||||
/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
|
||||
export default defineConfig({
|
||||
root: __dirname,
|
||||
cacheDir: '../../node_modules/.vite/apps/myapp',
|
||||
|
||||
server: {
|
||||
port: 4200,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
preview: {
|
||||
port: 4300,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
plugins: [nxViteTsPaths({ buildLibsFromSource: options.buildLibsFromSource }),react()],
|
||||
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
|
||||
build: {
|
||||
outDir: '../../dist/apps/myapp',
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
},
|
||||
|
||||
test: {
|
||||
globals: true,
|
||||
cache: {
|
||||
dir: '../../node_modules/.vitest',
|
||||
},
|
||||
environment: 'jsdom',
|
||||
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: '../../coverage/apps/myapp',
|
||||
provider: 'v8',
|
||||
},
|
||||
},
|
||||
});"
|
||||
`;
|
||||
|
||||
exports[`buildPostTargetTransformer moveBuildLibsFromSourceToViteConfig should add buildLibsFromSource to new nxViteTsPaths plugin when the plugins property does not exist 1`] = `
|
||||
"import { nxViteTsPaths } from "@nx/vite/plugins/nx-tsconfig-paths.plugin";
|
||||
/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
|
||||
export default defineConfig({plugins: [nxViteTsPaths({ buildLibsFromSource: options.buildLibsFromSource }),],
|
||||
root: __dirname,
|
||||
cacheDir: '../../node_modules/.vite/apps/myapp',
|
||||
|
||||
server: {
|
||||
port: 4200,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
preview: {
|
||||
port: 4300,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
|
||||
build: {
|
||||
outDir: '../../dist/apps/myapp',
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
},
|
||||
|
||||
test: {
|
||||
globals: true,
|
||||
cache: {
|
||||
dir: '../../node_modules/.vitest',
|
||||
},
|
||||
environment: 'jsdom',
|
||||
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: '../../coverage/apps/myapp',
|
||||
provider: 'v8',
|
||||
},
|
||||
},
|
||||
});"
|
||||
`;
|
||||
|
||||
exports[`buildPostTargetTransformer should remove the correct options and move the AST options to the vite config file correctly and remove outputs when they match inferred 1`] = `
|
||||
"/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
|
||||
|
||||
// These options were migrated by @nx/vite:convert-to-inferred from the project.json file.
|
||||
const configValues = {"default":{"buildLibsFromSource":true}};
|
||||
|
||||
// Determine the correct configValue to use based on the configuration
|
||||
const nxConfiguration = process.env.NX_TASK_TARGET_CONFIGURATION ?? 'default';
|
||||
|
||||
const options = {
|
||||
...configValues.default,
|
||||
...(configValues[nxConfiguration] ?? {})
|
||||
}
|
||||
|
||||
|
||||
export default defineConfig({
|
||||
root: __dirname,
|
||||
cacheDir: '../../node_modules/.vite/apps/myapp',
|
||||
|
||||
server: {
|
||||
port: 4200,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
preview: {
|
||||
port: 4300,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
plugins: [react(), nxViteTsPaths({ buildLibsFromSource: options.buildLibsFromSource }),],
|
||||
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
|
||||
build: {
|
||||
outDir: '../../dist/apps/myapp',
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
},
|
||||
|
||||
test: {
|
||||
globals: true,
|
||||
cache: {
|
||||
dir: '../../node_modules/.vitest',
|
||||
},
|
||||
environment: 'jsdom',
|
||||
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: '../../coverage/apps/myapp',
|
||||
provider: 'v8',
|
||||
},
|
||||
},
|
||||
});"
|
||||
`;
|
||||
@ -0,0 +1,414 @@
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import {
|
||||
buildPostTargetTransformer,
|
||||
moveBuildLibsFromSourceToViteConfig,
|
||||
} from './build-post-target-transformer';
|
||||
|
||||
describe('buildPostTargetTransformer', () => {
|
||||
it('should remove the correct options and move the AST options to the vite config file correctly and remove outputs when they match inferred', () => {
|
||||
// ARRANGE
|
||||
const tree = createTreeWithEmptyWorkspace();
|
||||
|
||||
const targetConfiguration = {
|
||||
outputs: ['{options.outputPath}'],
|
||||
options: {
|
||||
outputPath: 'build/apps/myapp',
|
||||
configFile: 'vite.config.ts',
|
||||
buildLibsFromSource: true,
|
||||
skipTypeCheck: false,
|
||||
watch: true,
|
||||
generatePackageJson: true,
|
||||
includeDevDependenciesInPackageJson: false,
|
||||
tsConfig: 'apps/myapp/tsconfig.json',
|
||||
},
|
||||
};
|
||||
|
||||
const inferredTargetConfiguration = {
|
||||
outputs: ['{projectRoot}/{options.outDir}'],
|
||||
};
|
||||
|
||||
tree.write('vite.config.ts', viteConfigFileV17);
|
||||
|
||||
// ACT
|
||||
const target = buildPostTargetTransformer(
|
||||
targetConfiguration,
|
||||
tree,
|
||||
{
|
||||
projectName: 'myapp',
|
||||
root: 'apps/myapp',
|
||||
},
|
||||
inferredTargetConfiguration
|
||||
);
|
||||
|
||||
// ASSERT
|
||||
const configFile = tree.read('vite.config.ts', 'utf-8');
|
||||
expect(configFile).toMatchSnapshot();
|
||||
expect(target).toMatchInlineSnapshot(`
|
||||
{
|
||||
"options": {
|
||||
"config": "../../vite.config.ts",
|
||||
"outDir": "../../build/apps/myapp",
|
||||
"watch": true,
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should add inferred outputs when a custom output exists', () => {
|
||||
// ARRANGE
|
||||
const tree = createTreeWithEmptyWorkspace();
|
||||
|
||||
const targetConfiguration = {
|
||||
outputs: ['{options.outputPath}', '{workspaceRoot}/my/custom/path'],
|
||||
options: {
|
||||
outputPath: 'build/apps/myapp',
|
||||
configFile: 'vite.config.ts',
|
||||
buildLibsFromSource: true,
|
||||
skipTypeCheck: false,
|
||||
watch: true,
|
||||
generatePackageJson: true,
|
||||
includeDevDependenciesInPackageJson: false,
|
||||
tsConfig: 'apps/myapp/tsconfig.json',
|
||||
},
|
||||
};
|
||||
|
||||
const inferredTargetConfiguration = {
|
||||
outputs: ['{projectRoot}/{options.outDir}'],
|
||||
};
|
||||
|
||||
tree.write('vite.config.ts', viteConfigFileV17);
|
||||
|
||||
// ACT
|
||||
const target = buildPostTargetTransformer(
|
||||
targetConfiguration,
|
||||
tree,
|
||||
{
|
||||
projectName: 'myapp',
|
||||
root: 'apps/myapp',
|
||||
},
|
||||
inferredTargetConfiguration
|
||||
);
|
||||
|
||||
// ASSERT
|
||||
expect(target).toMatchInlineSnapshot(`
|
||||
{
|
||||
"options": {
|
||||
"config": "../../vite.config.ts",
|
||||
"outDir": "../../build/apps/myapp",
|
||||
"watch": true,
|
||||
},
|
||||
"outputs": [
|
||||
"{projectRoot}/{options.outDir}",
|
||||
"{workspaceRoot}/my/custom/path",
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
describe('moveBuildLibsFromSourceToViteConfig', () => {
|
||||
it('should add buildLibsFromSource to existing nxViteTsPaths plugin with no existing options', () => {
|
||||
// ARRANGE
|
||||
const tree = createTreeWithEmptyWorkspace();
|
||||
tree.write('vite.config.ts', viteConfigFileV17);
|
||||
|
||||
// ACT
|
||||
moveBuildLibsFromSourceToViteConfig(tree, 'vite.config.ts');
|
||||
|
||||
// ASSERT
|
||||
const newContents = tree.read('vite.config.ts', 'utf-8');
|
||||
expect(newContents).toContain(
|
||||
'nxViteTsPaths({ buildLibsFromSource: options.buildLibsFromSource })'
|
||||
);
|
||||
expect(newContents).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should add buildLibsFromSource to existing nxViteTsPaths plugin with existing options', () => {
|
||||
// ARRANGE
|
||||
const tree = createTreeWithEmptyWorkspace();
|
||||
tree.write('vite.config.ts', viteConfigFileV17NxViteTsPathsOpts);
|
||||
|
||||
// ACT
|
||||
moveBuildLibsFromSourceToViteConfig(tree, 'vite.config.ts');
|
||||
|
||||
// ASSERT
|
||||
const newContents = tree.read('vite.config.ts', 'utf-8');
|
||||
expect(newContents).toContain(
|
||||
'nxViteTsPaths({buildLibsFromSource: options.buildLibsFromSource, debug: true })'
|
||||
);
|
||||
expect(newContents).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should add buildLibsFromSource to new nxViteTsPaths plugin when the plugin is not added', () => {
|
||||
// ARRANGE
|
||||
const tree = createTreeWithEmptyWorkspace();
|
||||
tree.write('vite.config.ts', viteConfigFileV17NoNxViteTsPaths);
|
||||
|
||||
// ACT
|
||||
moveBuildLibsFromSourceToViteConfig(tree, 'vite.config.ts');
|
||||
|
||||
// ASSERT
|
||||
const newContents = tree.read('vite.config.ts', 'utf-8');
|
||||
expect(newContents).toContain(
|
||||
'nxViteTsPaths({ buildLibsFromSource: options.buildLibsFromSource })'
|
||||
);
|
||||
expect(newContents).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should add buildLibsFromSource to new nxViteTsPaths plugin when the plugins property does not exist', () => {
|
||||
// ARRANGE
|
||||
const tree = createTreeWithEmptyWorkspace();
|
||||
tree.write('vite.config.ts', viteConfigFileV17NoPlugins);
|
||||
|
||||
// ACT
|
||||
moveBuildLibsFromSourceToViteConfig(tree, 'vite.config.ts');
|
||||
|
||||
// ASSERT
|
||||
const newContents = tree.read('vite.config.ts', 'utf-8');
|
||||
expect(newContents).toContain(
|
||||
'nxViteTsPaths({ buildLibsFromSource: options.buildLibsFromSource })'
|
||||
);
|
||||
expect(newContents).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const viteConfigFileV17 = `/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
|
||||
|
||||
export default defineConfig({
|
||||
root: __dirname,
|
||||
cacheDir: '../../node_modules/.vite/apps/myapp',
|
||||
|
||||
server: {
|
||||
port: 4200,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
preview: {
|
||||
port: 4300,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
plugins: [react(), nxViteTsPaths()],
|
||||
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
|
||||
build: {
|
||||
outDir: '../../dist/apps/myapp',
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
},
|
||||
|
||||
test: {
|
||||
globals: true,
|
||||
cache: {
|
||||
dir: '../../node_modules/.vitest',
|
||||
},
|
||||
environment: 'jsdom',
|
||||
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: '../../coverage/apps/myapp',
|
||||
provider: 'v8',
|
||||
},
|
||||
},
|
||||
});`;
|
||||
const viteConfigFileV17NoOutDir = `/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
|
||||
|
||||
export default defineConfig({
|
||||
root: __dirname,
|
||||
cacheDir: '../../node_modules/.vite/apps/myapp',
|
||||
|
||||
server: {
|
||||
port: 4200,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
preview: {
|
||||
port: 4300,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
plugins: [react(), nxViteTsPaths()],
|
||||
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
|
||||
build: {
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
},
|
||||
|
||||
test: {
|
||||
globals: true,
|
||||
cache: {
|
||||
dir: '../../node_modules/.vitest',
|
||||
},
|
||||
environment: 'jsdom',
|
||||
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: '../../coverage/apps/myapp',
|
||||
provider: 'v8',
|
||||
},
|
||||
},
|
||||
});`;
|
||||
const viteConfigFileV17NxViteTsPathsOpts = `/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
|
||||
|
||||
export default defineConfig({
|
||||
root: __dirname,
|
||||
cacheDir: '../../node_modules/.vite/apps/myapp',
|
||||
|
||||
server: {
|
||||
port: 4200,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
preview: {
|
||||
port: 4300,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
plugins: [react(), nxViteTsPaths({ debug: true })],
|
||||
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
|
||||
build: {
|
||||
outDir: '../../dist/apps/myapp',
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
},
|
||||
|
||||
test: {
|
||||
globals: true,
|
||||
cache: {
|
||||
dir: '../../node_modules/.vitest',
|
||||
},
|
||||
environment: 'jsdom',
|
||||
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: '../../coverage/apps/myapp',
|
||||
provider: 'v8',
|
||||
},
|
||||
},
|
||||
});`;
|
||||
const viteConfigFileV17NoNxViteTsPaths = `/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
|
||||
export default defineConfig({
|
||||
root: __dirname,
|
||||
cacheDir: '../../node_modules/.vite/apps/myapp',
|
||||
|
||||
server: {
|
||||
port: 4200,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
preview: {
|
||||
port: 4300,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
plugins: [react()],
|
||||
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
|
||||
build: {
|
||||
outDir: '../../dist/apps/myapp',
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
},
|
||||
|
||||
test: {
|
||||
globals: true,
|
||||
cache: {
|
||||
dir: '../../node_modules/.vitest',
|
||||
},
|
||||
environment: 'jsdom',
|
||||
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: '../../coverage/apps/myapp',
|
||||
provider: 'v8',
|
||||
},
|
||||
},
|
||||
});`;
|
||||
const viteConfigFileV17NoPlugins = `/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
|
||||
export default defineConfig({
|
||||
root: __dirname,
|
||||
cacheDir: '../../node_modules/.vite/apps/myapp',
|
||||
|
||||
server: {
|
||||
port: 4200,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
preview: {
|
||||
port: 4300,
|
||||
host: 'localhost',
|
||||
},
|
||||
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
|
||||
build: {
|
||||
outDir: '../../dist/apps/myapp',
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
},
|
||||
|
||||
test: {
|
||||
globals: true,
|
||||
cache: {
|
||||
dir: '../../node_modules/.vitest',
|
||||
},
|
||||
environment: 'jsdom',
|
||||
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: '../../coverage/apps/myapp',
|
||||
provider: 'v8',
|
||||
},
|
||||
},
|
||||
});`;
|
||||
@ -0,0 +1,234 @@
|
||||
import { type TargetConfiguration, type Tree } from '@nx/devkit';
|
||||
import { tsquery } from '@phenomnomnominal/tsquery';
|
||||
import { extname } from 'path/posix';
|
||||
import {
|
||||
addConfigValuesToViteConfig,
|
||||
getViteConfigPath,
|
||||
toProjectRelativePath,
|
||||
} from './utils';
|
||||
import { processTargetOutputs } from '@nx/devkit/src/generators/plugin-migrations/plugin-migration-utils';
|
||||
|
||||
export function buildPostTargetTransformer(
|
||||
target: TargetConfiguration,
|
||||
tree: Tree,
|
||||
projectDetails: { projectName: string; root: string },
|
||||
inferredTargetConfiguration: TargetConfiguration
|
||||
) {
|
||||
let viteConfigPath = getViteConfigPath(tree, projectDetails.root);
|
||||
|
||||
const configValues: Record<string, Record<string, unknown>> = {
|
||||
default: {},
|
||||
};
|
||||
|
||||
if (target.options) {
|
||||
if (target.options.configFile) {
|
||||
viteConfigPath = target.options.configFile;
|
||||
}
|
||||
|
||||
removePropertiesFromTargetOptions(
|
||||
tree,
|
||||
target.options,
|
||||
viteConfigPath,
|
||||
projectDetails.root,
|
||||
configValues['default'],
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
if (target.configurations) {
|
||||
for (const configurationName in target.configurations) {
|
||||
const configuration = target.configurations[configurationName];
|
||||
configValues[configuration] = {};
|
||||
removePropertiesFromTargetOptions(
|
||||
tree,
|
||||
configuration,
|
||||
viteConfigPath,
|
||||
projectDetails.root,
|
||||
configValues[configuration]
|
||||
);
|
||||
|
||||
if (Object.keys(configuration).length === 0) {
|
||||
delete target.configurations[configurationName];
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(target.configurations).length === 0) {
|
||||
if ('defaultConfiguration' in target) {
|
||||
delete target.defaultConfiguration;
|
||||
}
|
||||
delete target.configurations;
|
||||
}
|
||||
|
||||
if (
|
||||
'defaultConfiguration' in target &&
|
||||
!target.configurations[target.defaultConfiguration]
|
||||
) {
|
||||
delete target.defaultConfiguration;
|
||||
}
|
||||
}
|
||||
|
||||
if (target.outputs) {
|
||||
processTargetOutputs(
|
||||
target,
|
||||
[{ newName: 'outDir', oldName: 'outputPath' }],
|
||||
inferredTargetConfiguration,
|
||||
{
|
||||
projectName: projectDetails.projectName,
|
||||
projectRoot: projectDetails.root,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
target.inputs &&
|
||||
target.inputs.every((i) => i === 'production' || i === '^production')
|
||||
) {
|
||||
delete target.inputs;
|
||||
}
|
||||
|
||||
addConfigValuesToViteConfig(tree, viteConfigPath, configValues);
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
function removePropertiesFromTargetOptions(
|
||||
tree: Tree,
|
||||
targetOptions: any,
|
||||
viteConfigPath: string,
|
||||
projectRoot: string,
|
||||
configValues: Record<string, unknown>,
|
||||
defaultOptions = false
|
||||
) {
|
||||
if ('configFile' in targetOptions) {
|
||||
targetOptions.config = toProjectRelativePath(
|
||||
targetOptions.configFile,
|
||||
projectRoot
|
||||
);
|
||||
delete targetOptions.configFile;
|
||||
}
|
||||
if (targetOptions.outputPath) {
|
||||
targetOptions.outDir = toProjectRelativePath(
|
||||
targetOptions.outputPath,
|
||||
projectRoot
|
||||
);
|
||||
|
||||
delete targetOptions.outputPath;
|
||||
}
|
||||
if ('buildLibsFromSource' in targetOptions) {
|
||||
configValues['buildLibsFromSource'] = targetOptions.buildLibsFromSource;
|
||||
|
||||
if (defaultOptions) {
|
||||
moveBuildLibsFromSourceToViteConfig(tree, viteConfigPath);
|
||||
}
|
||||
delete targetOptions.buildLibsFromSource;
|
||||
}
|
||||
if ('skipTypeCheck' in targetOptions) {
|
||||
delete targetOptions.skipTypeCheck;
|
||||
}
|
||||
if ('generatePackageJson' in targetOptions) {
|
||||
delete targetOptions.generatePackageJson;
|
||||
}
|
||||
if ('includeDevDependenciesInPackageJson' in targetOptions) {
|
||||
delete targetOptions.includeDevDependenciesInPackageJson;
|
||||
}
|
||||
if ('tsConfig' in targetOptions) {
|
||||
delete targetOptions.tsConfig;
|
||||
}
|
||||
}
|
||||
|
||||
export function moveBuildLibsFromSourceToViteConfig(
|
||||
tree: Tree,
|
||||
configPath: string
|
||||
) {
|
||||
const PLUGINS_PROPERTY_SELECTOR =
|
||||
'PropertyAssignment:has(Identifier[name=plugins])';
|
||||
const PLUGINS_NX_VITE_TS_PATHS_SELECTOR =
|
||||
'PropertyAssignment:has(Identifier[name=plugins]) CallExpression:has(Identifier[name=nxViteTsPaths])';
|
||||
const BUILD_LIBS_FROM_SOURCE_SELECTOR =
|
||||
'PropertyAssignment:has(Identifier[name=plugins]) CallExpression:has(Identifier[name=nxViteTsPaths]) ObjectLiteralExpression > PropertyAssignment:has(Identifier[name=buildLibsFromSource])';
|
||||
|
||||
const nxViteTsPathsImport =
|
||||
extname(configPath) === 'js'
|
||||
? 'const {nxViteTsPaths} = require("@nx/vite/plugins/nx-tsconfig-paths.plugin");'
|
||||
: 'import { nxViteTsPaths } from "@nx/vite/plugins/nx-tsconfig-paths.plugin";';
|
||||
const plugin = `nxViteTsPaths({ buildLibsFromSource: options.buildLibsFromSource }),`;
|
||||
|
||||
const viteConfigContents = tree.read(configPath, 'utf-8');
|
||||
let newViteConfigContents = viteConfigContents;
|
||||
|
||||
const ast = tsquery.ast(viteConfigContents);
|
||||
const buildLibsFromSourceNodes = tsquery(
|
||||
ast,
|
||||
BUILD_LIBS_FROM_SOURCE_SELECTOR,
|
||||
{ visitAllChildren: true }
|
||||
);
|
||||
if (buildLibsFromSourceNodes.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nxViteTsPathsNodes = tsquery(ast, PLUGINS_NX_VITE_TS_PATHS_SELECTOR, {
|
||||
visitAllChildren: true,
|
||||
});
|
||||
if (nxViteTsPathsNodes.length === 0) {
|
||||
const pluginsNodes = tsquery(ast, PLUGINS_PROPERTY_SELECTOR, {
|
||||
visitAllChildren: true,
|
||||
});
|
||||
if (pluginsNodes.length === 0) {
|
||||
// Add plugin property
|
||||
const configNodes = tsquery(
|
||||
ast,
|
||||
'CallExpression:has(Identifier[name=defineConfig]) > ObjectLiteralExpression',
|
||||
{ visitAllChildren: true }
|
||||
);
|
||||
if (configNodes.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
newViteConfigContents = `${nxViteTsPathsImport}\n${viteConfigContents.slice(
|
||||
0,
|
||||
configNodes[0].getStart() + 1
|
||||
)}plugins: [${plugin}],${viteConfigContents.slice(
|
||||
configNodes[0].getStart() + 1
|
||||
)}`;
|
||||
} else {
|
||||
// Add nxViteTsPaths plugin
|
||||
|
||||
const pluginsArrayNodes = tsquery(
|
||||
pluginsNodes[0],
|
||||
'ArrayLiteralExpression'
|
||||
);
|
||||
if (pluginsArrayNodes.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
newViteConfigContents = `${nxViteTsPathsImport}\n${viteConfigContents.slice(
|
||||
0,
|
||||
pluginsArrayNodes[0].getStart() + 1
|
||||
)}${plugin}${viteConfigContents.slice(
|
||||
pluginsArrayNodes[0].getStart() + 1
|
||||
)}`;
|
||||
}
|
||||
} else {
|
||||
const pluginOptionsNodes = tsquery(
|
||||
nxViteTsPathsNodes[0],
|
||||
'ObjectLiteralExpression'
|
||||
);
|
||||
if (pluginOptionsNodes.length === 0) {
|
||||
// Add the options
|
||||
newViteConfigContents = `${viteConfigContents.slice(
|
||||
0,
|
||||
nxViteTsPathsNodes[0].getStart()
|
||||
)}${plugin}${viteConfigContents.slice(nxViteTsPathsNodes[0].getEnd())}`;
|
||||
} else {
|
||||
// update the object
|
||||
newViteConfigContents = `${viteConfigContents.slice(
|
||||
0,
|
||||
pluginOptionsNodes[0].getStart() + 1
|
||||
)}buildLibsFromSource: options.buildLibsFromSource, ${viteConfigContents.slice(
|
||||
pluginOptionsNodes[0].getStart() + 1
|
||||
)}`;
|
||||
}
|
||||
}
|
||||
|
||||
tree.write(configPath, newViteConfigContents);
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
import { type TargetConfiguration, type Tree } from '@nx/devkit';
|
||||
import { getViteConfigPath } from './utils';
|
||||
import { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util';
|
||||
|
||||
export function previewPostTargetTransformer(migrationLogs: AggregatedLog) {
|
||||
return (
|
||||
target: TargetConfiguration,
|
||||
tree: Tree,
|
||||
projectDetails: { projectName: string; root: string },
|
||||
inferredTargetConfiguration: TargetConfiguration
|
||||
) => {
|
||||
const viteConfigPath = getViteConfigPath(tree, projectDetails.root);
|
||||
|
||||
if (target.options) {
|
||||
removePropertiesFromTargetOptions(
|
||||
target.options,
|
||||
projectDetails.projectName,
|
||||
migrationLogs
|
||||
);
|
||||
}
|
||||
|
||||
if (target.configurations) {
|
||||
for (const configurationName in target.configurations) {
|
||||
const configuration = target.configurations[configurationName];
|
||||
removePropertiesFromTargetOptions(
|
||||
configuration,
|
||||
projectDetails.projectName,
|
||||
migrationLogs
|
||||
);
|
||||
|
||||
if (Object.keys(configuration).length === 0) {
|
||||
delete target.configurations[configurationName];
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(target.configurations).length === 0) {
|
||||
if ('defaultConfiguration' in target) {
|
||||
delete target.defaultConfiguration;
|
||||
}
|
||||
delete target.configurations;
|
||||
}
|
||||
|
||||
if (
|
||||
'defaultConfiguration' in target &&
|
||||
!target.configurations[target.defaultConfiguration]
|
||||
) {
|
||||
delete target.defaultConfiguration;
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
};
|
||||
}
|
||||
|
||||
function removePropertiesFromTargetOptions(
|
||||
targetOptions: any,
|
||||
projectName: string,
|
||||
migrationLogs: AggregatedLog
|
||||
) {
|
||||
if ('buildTarget' in targetOptions) {
|
||||
delete targetOptions.buildTarget;
|
||||
}
|
||||
|
||||
if ('staticFilePath' in targetOptions) {
|
||||
delete targetOptions.staticFilePath;
|
||||
}
|
||||
|
||||
if ('proxyConfig' in targetOptions) {
|
||||
migrationLogs.addLog({
|
||||
executorName: '@nx/vite:preview-server',
|
||||
project: projectName,
|
||||
log: `Encountered 'proxyConfig' in project.json. You will need to copy the contents of this file to the 'server.proxy' property in your Vite config file.`,
|
||||
});
|
||||
delete targetOptions.proxyConfig;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,96 @@
|
||||
import { type TargetConfiguration, type Tree } from '@nx/devkit';
|
||||
import { getViteConfigPath } from './utils';
|
||||
import { AggregatedLog } from '@nx/devkit/src/generators/plugin-migrations/aggregate-log-util';
|
||||
|
||||
export function servePostTargetTransformer(migrationLogs: AggregatedLog) {
|
||||
return (
|
||||
target: TargetConfiguration,
|
||||
tree: Tree,
|
||||
projectDetails: { projectName: string; root: string },
|
||||
inferredTargetConfiguration: TargetConfiguration
|
||||
) => {
|
||||
const viteConfigPath = getViteConfigPath(tree, projectDetails.root);
|
||||
|
||||
if (target.options) {
|
||||
removePropertiesFromTargetOptions(
|
||||
tree,
|
||||
target.options,
|
||||
viteConfigPath,
|
||||
projectDetails.root,
|
||||
projectDetails.projectName,
|
||||
migrationLogs,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
if (target.configurations) {
|
||||
for (const configurationName in target.configurations) {
|
||||
const configuration = target.configurations[configurationName];
|
||||
removePropertiesFromTargetOptions(
|
||||
tree,
|
||||
configuration,
|
||||
viteConfigPath,
|
||||
projectDetails.root,
|
||||
projectDetails.projectName,
|
||||
migrationLogs
|
||||
);
|
||||
|
||||
if (Object.keys(configuration).length === 0) {
|
||||
delete target.configurations[configurationName];
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(target.configurations).length === 0) {
|
||||
if ('defaultConfiguration' in target) {
|
||||
delete target.defaultConfiguration;
|
||||
}
|
||||
delete target.configurations;
|
||||
}
|
||||
|
||||
if (
|
||||
'defaultConfiguration' in target &&
|
||||
!target.configurations[target.defaultConfiguration]
|
||||
) {
|
||||
delete target.defaultConfiguration;
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
};
|
||||
}
|
||||
|
||||
function removePropertiesFromTargetOptions(
|
||||
tree: Tree,
|
||||
targetOptions: any,
|
||||
viteConfigPath: string,
|
||||
projectRoot: string,
|
||||
projectName: string,
|
||||
migrationLogs: AggregatedLog,
|
||||
defaultOptions = false
|
||||
) {
|
||||
if ('buildTarget' in targetOptions) {
|
||||
delete targetOptions.buildTarget;
|
||||
}
|
||||
|
||||
if ('buildLibsFromSource' in targetOptions) {
|
||||
migrationLogs.addLog({
|
||||
executorName: '@nx/vite:dev-server',
|
||||
project: projectName,
|
||||
log: `Encountered 'buildLibsFromSource' in project.json. This property will be added to your Vite config file via the '@nx/vite:build' executor migration.`,
|
||||
});
|
||||
delete targetOptions.buildLibsFromSource;
|
||||
}
|
||||
|
||||
if ('hmr' in targetOptions) {
|
||||
delete targetOptions.hmr;
|
||||
}
|
||||
|
||||
if ('proxyConfig' in targetOptions) {
|
||||
migrationLogs.addLog({
|
||||
executorName: '@nx/vite:dev-server',
|
||||
project: projectName,
|
||||
log: `Encountered 'proxyConfig' in project.json. You will need to copy the contents of this file to the 'server.proxy' property in your Vite config file.`,
|
||||
});
|
||||
delete targetOptions.proxyConfig;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,94 @@
|
||||
import { type TargetConfiguration, type Tree } from '@nx/devkit';
|
||||
import { toProjectRelativePath } from './utils';
|
||||
import { processTargetOutputs } from '@nx/devkit/src/generators/plugin-migrations/plugin-migration-utils';
|
||||
|
||||
export function testPostTargetTransformer(
|
||||
target: TargetConfiguration,
|
||||
tree: Tree,
|
||||
projectDetails: { projectName: string; root: string },
|
||||
inferredTargetConfiguration: TargetConfiguration
|
||||
) {
|
||||
if (target.options) {
|
||||
removePropertiesFromTargetOptions(target.options, projectDetails.root);
|
||||
}
|
||||
|
||||
if (target.configurations) {
|
||||
for (const configurationName in target.configurations) {
|
||||
const configuration = target.configurations[configurationName];
|
||||
removePropertiesFromTargetOptions(configuration, projectDetails.root);
|
||||
|
||||
if (Object.keys(configuration).length === 0) {
|
||||
delete target.configurations[configurationName];
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(target.configurations).length === 0) {
|
||||
if ('defaultConfiguration' in target) {
|
||||
delete target.defaultConfiguration;
|
||||
}
|
||||
delete target.configurations;
|
||||
}
|
||||
|
||||
if (
|
||||
'defaultConfiguration' in target &&
|
||||
!target.configurations[target.defaultConfiguration]
|
||||
) {
|
||||
delete target.defaultConfiguration;
|
||||
}
|
||||
}
|
||||
|
||||
if (target.outputs) {
|
||||
processTargetOutputs(
|
||||
target,
|
||||
[{ newName: 'coverage.reportsDirectory', oldName: 'reportsDirectory' }],
|
||||
inferredTargetConfiguration,
|
||||
{
|
||||
projectName: projectDetails.projectName,
|
||||
projectRoot: projectDetails.root,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
target.inputs &&
|
||||
target.inputs.every((i) => i === 'default' || i === '^production')
|
||||
) {
|
||||
delete target.inputs;
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
function removePropertiesFromTargetOptions(
|
||||
targetOptions: any,
|
||||
projectRoot: string
|
||||
) {
|
||||
if ('configFile' in targetOptions) {
|
||||
targetOptions.config = toProjectRelativePath(
|
||||
targetOptions.configFile,
|
||||
projectRoot
|
||||
);
|
||||
delete targetOptions.configFile;
|
||||
}
|
||||
|
||||
if ('reportsDirectory' in targetOptions) {
|
||||
if (targetOptions.reportsDirectory.startsWith('../')) {
|
||||
targetOptions.reportsDirectory = targetOptions.reportsDirectory.replace(
|
||||
/(\.\.\/)+/,
|
||||
''
|
||||
);
|
||||
}
|
||||
targetOptions['coverage.reportsDirectory'] = toProjectRelativePath(
|
||||
targetOptions.reportsDirectory,
|
||||
projectRoot
|
||||
);
|
||||
delete targetOptions.reportsDirectory;
|
||||
}
|
||||
|
||||
if ('testFiles' in targetOptions) {
|
||||
targetOptions.testNamePattern = `"/(${targetOptions.testFiles
|
||||
.map((f) => f.replace('.', '\\.'))
|
||||
.join('|')})/"`;
|
||||
delete targetOptions.testFiles;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
import { relative, resolve } from 'path/posix';
|
||||
import { workspaceRoot, type Tree, joinPathFragments } from '@nx/devkit';
|
||||
import { tsquery } from '@phenomnomnominal/tsquery';
|
||||
|
||||
export function toProjectRelativePath(
|
||||
path: string,
|
||||
projectRoot: string
|
||||
): string {
|
||||
if (projectRoot === '.') {
|
||||
// workspace and project root are the same, we normalize it to ensure it
|
||||
// works with Jest since some paths only work when they start with `./`
|
||||
return path.startsWith('.') ? path : `./${path}`;
|
||||
}
|
||||
|
||||
const relativePath = relative(
|
||||
resolve(workspaceRoot, projectRoot),
|
||||
resolve(workspaceRoot, path)
|
||||
);
|
||||
|
||||
return relativePath.startsWith('.') ? relativePath : `./${relativePath}`;
|
||||
}
|
||||
|
||||
export function getViteConfigPath(tree: Tree, root: string) {
|
||||
return [
|
||||
joinPathFragments(root, `vite.config.ts`),
|
||||
joinPathFragments(root, `vite.config.cts`),
|
||||
joinPathFragments(root, `vite.config.mts`),
|
||||
joinPathFragments(root, `vite.config.js`),
|
||||
joinPathFragments(root, `vite.config.cjs`),
|
||||
joinPathFragments(root, `vite.config.mjs`),
|
||||
].find((f) => tree.exists(f));
|
||||
}
|
||||
|
||||
export function addConfigValuesToViteConfig(
|
||||
tree: Tree,
|
||||
configFile: string,
|
||||
configValues: Record<string, Record<string, unknown>>
|
||||
) {
|
||||
const IMPORT_PROPERTY_SELECTOR = 'ImportDeclaration';
|
||||
const viteConfigContents = tree.read(configFile, 'utf-8');
|
||||
|
||||
const ast = tsquery.ast(viteConfigContents);
|
||||
// AST TO GET SECTION TO APPEND TO
|
||||
const importNodes = tsquery(ast, IMPORT_PROPERTY_SELECTOR, {
|
||||
visitAllChildren: true,
|
||||
});
|
||||
if (importNodes.length === 0) {
|
||||
return;
|
||||
}
|
||||
const lastImportNode = importNodes[importNodes.length - 1];
|
||||
|
||||
const configValuesString = `
|
||||
// These options were migrated by @nx/vite:convert-to-inferred from the project.json file.
|
||||
const configValues = ${JSON.stringify(configValues)};
|
||||
|
||||
// Determine the correct configValue to use based on the configuration
|
||||
const nxConfiguration = process.env.NX_TASK_TARGET_CONFIGURATION ?? 'default';
|
||||
|
||||
const options = {
|
||||
...configValues.default,
|
||||
...(configValues[nxConfiguration] ?? {})
|
||||
}`;
|
||||
|
||||
tree.write(
|
||||
configFile,
|
||||
`${viteConfigContents.slice(0, lastImportNode.getEnd())}
|
||||
${configValuesString}
|
||||
${viteConfigContents.slice(lastImportNode.getEnd())}`
|
||||
);
|
||||
}
|
||||
19
packages/vite/src/generators/convert-to-inferred/schema.json
Normal file
19
packages/vite/src/generators/convert-to-inferred/schema.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/schema",
|
||||
"$id": "NxViteConvertToInferred",
|
||||
"description": "Convert existing Vite project(s) using `@nx/vite:*` executors to use `@nx/vite/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target.",
|
||||
"title": "Convert Vite project from executor to plugin",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"project": {
|
||||
"type": "string",
|
||||
"description": "The project to convert from using the `@nx/vite:*` executors to use `@nx/vite/plugin`.",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"skipFormat": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to format files at the end of the migration.",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,8 +14,7 @@ describe('@nx/vite/plugin', () => {
|
||||
let context: CreateNodesContext;
|
||||
|
||||
describe('root project', () => {
|
||||
let tempFs;
|
||||
|
||||
let tempFs: TempFs;
|
||||
beforeEach(async () => {
|
||||
tempFs = new TempFs('vite-plugin-tests');
|
||||
context = {
|
||||
@ -62,8 +61,7 @@ describe('@nx/vite/plugin', () => {
|
||||
});
|
||||
|
||||
describe('not root project', () => {
|
||||
let tempFs;
|
||||
|
||||
let tempFs: TempFs;
|
||||
beforeEach(() => {
|
||||
tempFs = new TempFs('test');
|
||||
context = {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user