Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5d3469a
feat(report): add frameworkShimAdded field to MigrationReport
naokihaba Apr 17, 2026
23fb9be
feat(framework): add framework detection and shim functionality for V…
naokihaba Apr 17, 2026
7b8f9ae
feat(migration): add framework shim confirmation and handling for Typ…
naokihaba Apr 17, 2026
95c9bf1
feat(framework): add framework detection and shim functionality for V…
naokihaba Apr 17, 2026
9870638
feat(migration): add initial configuration files for Vue framework shim
naokihaba Apr 17, 2026
a071bb8
docs(migrator): update comments regarding Svelte file handling and mo…
naokihaba Apr 17, 2026
0e816f8
chore: add .gitkeep file to migration-framework-shim-vue directory
naokihaba Apr 17, 2026
9ebcfd5
feat(migration): update snap.txt to reflect TypeScript shim addition …
naokihaba Apr 17, 2026
497308d
feat(migration): add initial Astro framework shim configuration and r…
naokihaba Apr 17, 2026
9bb73f6
feat(framework): add framework detection and shim installation during…
naokihaba Apr 17, 2026
760d754
feat(test): add integration tests for framework shim creation flow
naokihaba Apr 17, 2026
278796b
feat(framework): add initial Vue framework shim configuration and rel…
naokihaba Apr 17, 2026
38d5f0b
style(tests): format code for better readability in migrator.spec.ts
naokihaba Apr 17, 2026
82718b3
feat(astro): add initial steps and snapshot for Astro framework shim …
naokihaba Apr 17, 2026
4208936
refactor(framework): change detectFramework to return an array of fra…
naokihaba Apr 17, 2026
03a42b2
test(framework): update detectFramework tests to return arrays of fra…
naokihaba Apr 17, 2026
f8fb0f3
refactor(framework): update framework shim handling to support multip…
naokihaba Apr 17, 2026
7dafa42
feat(migration): add initial migration framework shim for Astro and Vue
naokihaba Apr 17, 2026
7a0e29b
style: reorder dependencies in package.json and format code for bette…
naokihaba Apr 17, 2026
7112e7e
feat(migration): enhance framework shim detection and addition for wo…
naokihaba Apr 17, 2026
d2624ad
fix(migration): ensure framework shim is added only if detected in th…
naokihaba Apr 17, 2026
64cfdaf
refactor(migration): improve framework shim detection logic for bette…
naokihaba Apr 17, 2026
75b1d19
style(migration): format code for better readability in collectMigrat…
naokihaba Apr 17, 2026
aa3b53b
Merge branch 'main' into main
naokihaba Apr 19, 2026
5997791
feat(snap-tests): add installation and formatting checks for Astro an…
naokihaba Apr 20, 2026
95619fa
Merge branch 'main' into main
naokihaba Apr 20, 2026
2eeefbc
Merge branch 'main' into main
fengmk2 Apr 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
> vp create astro --no-interactive -- my-astro-app --yes --skip-houston --template basics # create Astro app
> cat my-astro-app/src/env.d.ts # check Astro shim
/// <reference types="astro/client" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"ignoredPlatforms": ["win32"],
"commands": [
{
"command": "vp create astro --no-interactive -- my-astro-app --yes --skip-houston --template basics # create Astro app",
"ignoreOutput": true
},
"cat my-astro-app/src/env.d.ts # check Astro shim"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
> vp create vite:application --no-interactive -- --template vue-ts # create Vue+TS app
> cat vite-plus-application/src/env.d.ts # check Vue shim was added
declare module '*.vue' {
import type { DefineComponent } from 'vue';
const component: DefineComponent<{}, {}, unknown>;
export default component;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"ignoredPlatforms": ["win32"],
"commands": [
{
"command": "vp create vite:application --no-interactive -- --template vue-ts # create Vue+TS app",
"ignoreOutput": true
},
"cat vite-plus-application/src/env.d.ts # check Vue shim was added"
Comment thread
fengmk2 marked this conversation as resolved.
Outdated
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "migration-framework-shim-astro",
"devDependencies": {
"astro": "^4.0.0",
"vite": "^7.0.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
> vp migrate --no-interactive --no-hooks # migration should add Astro shim when astro dependency is detected
VITE+ - The Unified Toolchain for the Web

◇ Migrated . to Vite+<repeat>
• Node <semver> pnpm <semver>
✓ Dependencies installed in <variable>ms
• 1 config update applied
• TypeScript shim added for framework component files

> cat src/env.d.ts # check Astro shim was written
/// <reference types="astro/client" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"ignoredPlatforms": ["win32"],
"env": {
"VITE_DISABLE_AUTO_INSTALL": "1",
"CI": "",
"VP_SKIP_INSTALL": ""
},
"commands": [
"vp migrate --no-interactive --no-hooks # migration should add Astro shim when astro dependency is detected",
"cat src/env.d.ts # check Astro shim was written"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "migration-framework-shim-vue",
"devDependencies": {
"vite": "^7.0.0",
"vue": "^3.0.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
> vp migrate --no-interactive --no-hooks # migration should add Vue shim when vue dependency is detected
VITE+ - The Unified Toolchain for the Web

◇ Migrated . to Vite+<repeat>
• Node <semver> pnpm <semver>
✓ Dependencies installed in <variable>ms
• 1 config update applied
• TypeScript shim added for framework component files

> cat src/env.d.ts # check Vue shim was written
declare module '*.vue' {
import type { DefineComponent } from 'vue';
const component: DefineComponent<{}, {}, unknown>;
export default component;
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"ignoredPlatforms": ["win32"],
"env": {
"VITE_DISABLE_AUTO_INSTALL": "1",
"CI": "",
"VP_SKIP_INSTALL": ""
},
"commands": [
"vp migrate --no-interactive --no-hooks # migration should add Vue shim when vue dependency is detected",
"cat src/env.d.ts # check Vue shim was written"
]
}
7 changes: 7 additions & 0 deletions packages/cli/src/create/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import mri from 'mri';

import { vitePlusHeader } from '../../binding/index.js';
import {
addFrameworkShim,
detectFramework,
hasFrameworkShim,
installGitHooks,
rewriteMonorepo,
rewriteMonorepoProject,
Expand Down Expand Up @@ -968,6 +971,10 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
} else {
updateCreateProgress('Applying Vite+ project setup');
rewriteStandaloneProject(fullPath, workspaceInfo, undefined, compactOutput);
const createdFramework = detectFramework(fullPath);
if (createdFramework && !hasFrameworkShim(fullPath, createdFramework)) {
addFrameworkShim(fullPath, createdFramework);
}
Comment thread
naokihaba marked this conversation as resolved.
if (shouldSetupHooks) {
installGitHooks(fullPath, compactOutput);
}
Expand Down
175 changes: 175 additions & 0 deletions packages/cli/src/migration/__tests__/migrator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import path from 'node:path';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

import { PackageManager } from '../../types/index.js';
import { createMigrationReport } from '../report.js';

// Mock VITE_PLUS_VERSION to a stable value for snapshot tests.
// When tests run via `vp test`, the env var is injected with the actual version,
Expand All @@ -21,6 +22,9 @@ const {
parseNvmrcVersion,
detectNodeVersionManagerFile,
migrateNodeVersionManagerFile,
detectFramework,
hasFrameworkShim,
addFrameworkShim,
} = await import('../migrator.js');

describe('rewritePackageJson', () => {
Expand Down Expand Up @@ -304,6 +308,7 @@ describe('migrateNodeVersionManagerFile', () => {
prettierMigrated: false,
nodeVersionFileMigrated: false,
gitHooksConfigured: false,
frameworkShimAdded: false,
warnings: [],
manualSteps: [],
};
Expand Down Expand Up @@ -334,6 +339,7 @@ describe('migrateNodeVersionManagerFile', () => {
prettierMigrated: false,
nodeVersionFileMigrated: false,
gitHooksConfigured: false,
frameworkShimAdded: false,
warnings: [],
manualSteps: [],
};
Expand Down Expand Up @@ -366,6 +372,7 @@ describe('migrateNodeVersionManagerFile', () => {
prettierMigrated: false,
nodeVersionFileMigrated: false,
gitHooksConfigured: false,
frameworkShimAdded: false,
warnings: [],
manualSteps: [],
};
Expand Down Expand Up @@ -401,6 +408,7 @@ describe('migrateNodeVersionManagerFile', () => {
prettierMigrated: false,
nodeVersionFileMigrated: false,
gitHooksConfigured: false,
frameworkShimAdded: false,
warnings: [],
manualSteps: [],
};
Expand Down Expand Up @@ -638,3 +646,170 @@ describe('rewriteMonorepo bun catalog', () => {
expect(workspaces.packages).toEqual(['packages/*']);
});
});

describe('framework shim', () => {
let tmpDir: string;

beforeEach(() => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'vp-test-'));
});

afterEach(() => {
fs.rmSync(tmpDir, { recursive: true, force: true });
});

describe('detectFramework', () => {
it('returns vue when vue is in devDependencies', () => {
fs.writeFileSync(
path.join(tmpDir, 'package.json'),
JSON.stringify({ devDependencies: { vue: '^3.0.0' } }),
);
expect(detectFramework(tmpDir)).toBe('vue');
});

it('returns astro when astro is in devDependencies', () => {
fs.writeFileSync(
path.join(tmpDir, 'package.json'),
JSON.stringify({ devDependencies: { astro: '^4.0.0' } }),
);
expect(detectFramework(tmpDir)).toBe('astro');
});

it('returns null when no framework dependency is present', () => {
fs.writeFileSync(
path.join(tmpDir, 'package.json'),
JSON.stringify({ devDependencies: { vite: '^7.0.0' } }),
);
expect(detectFramework(tmpDir)).toBeNull();
});

it('returns null when package.json does not exist', () => {
expect(detectFramework(tmpDir)).toBeNull();
});
});

describe('hasFrameworkShim', () => {
it('returns true when src/env.d.ts contains vue shim', () => {
const srcDir = path.join(tmpDir, 'src');
fs.mkdirSync(srcDir);
fs.writeFileSync(
path.join(srcDir, 'env.d.ts'),
"declare module '*.vue' { export default {} }\n",
);
expect(hasFrameworkShim(tmpDir, 'vue')).toBe(true);
});

it('returns false when src/env.d.ts does not contain vue shim', () => {
const srcDir = path.join(tmpDir, 'src');
fs.mkdirSync(srcDir);
fs.writeFileSync(
path.join(srcDir, 'env.d.ts'),
'/// <reference types="vite-plus/client" />\n',
);
expect(hasFrameworkShim(tmpDir, 'vue')).toBe(false);
});

it('returns false when env.d.ts does not exist', () => {
expect(hasFrameworkShim(tmpDir, 'vue')).toBe(false);
});

it('returns true when root env.d.ts contains astro/client reference', () => {
fs.writeFileSync(path.join(tmpDir, 'env.d.ts'), '/// <reference types="astro/client" />\n');
expect(hasFrameworkShim(tmpDir, 'astro')).toBe(true);
});
});

describe('addFrameworkShim', () => {
it('creates src/env.d.ts with vue shim when src/ exists and no env.d.ts', () => {
fs.mkdirSync(path.join(tmpDir, 'src'));
addFrameworkShim(tmpDir, 'vue');
const content = fs.readFileSync(path.join(tmpDir, 'src', 'env.d.ts'), 'utf-8');
expect(content).toContain("declare module '*.vue'");
expect(content).toContain('DefineComponent');
});

it('creates root env.d.ts with vue shim when no src/ dir', () => {
addFrameworkShim(tmpDir, 'vue');
const content = fs.readFileSync(path.join(tmpDir, 'env.d.ts'), 'utf-8');
expect(content).toContain("declare module '*.vue'");
});

it('appends vue shim to existing src/env.d.ts', () => {
const srcDir = path.join(tmpDir, 'src');
fs.mkdirSync(srcDir);
const existing = '/// <reference types="vite-plus/client" />\n';
fs.writeFileSync(path.join(srcDir, 'env.d.ts'), existing);
addFrameworkShim(tmpDir, 'vue');
const content = fs.readFileSync(path.join(srcDir, 'env.d.ts'), 'utf-8');
expect(content).toContain('/// <reference types="vite-plus/client" />');
expect(content).toContain("declare module '*.vue'");
});

it('sets frameworkShimAdded on report', () => {
fs.mkdirSync(path.join(tmpDir, 'src'));
const report = createMigrationReport();
addFrameworkShim(tmpDir, 'vue', report);
expect(report.frameworkShimAdded).toBe(true);
});
});

describe('create flow integration', () => {
it('does not add duplicate shim when template already wrote env.d.ts', () => {
// Simulate create-vue having already written a shim into src/env.d.ts
const srcDir = path.join(tmpDir, 'src');
fs.mkdirSync(srcDir);
const existingShim =
"declare module '*.vue' {\n import type { DefineComponent } from 'vue';\n const component: DefineComponent;\n export default component;\n}\n";
fs.writeFileSync(path.join(srcDir, 'env.d.ts'), existingShim);

const framework = detectFramework(
(() => {
fs.writeFileSync(
path.join(tmpDir, 'package.json'),
JSON.stringify({ devDependencies: { vue: '^3.0.0' } }),
);
return tmpDir;
})(),
);
expect(framework).toBe('vue');
// Gate check: shim already present, so addFrameworkShim should NOT be called
expect(hasFrameworkShim(tmpDir, 'vue')).toBe(true);
// Verify content is unchanged if caller respects the gate
const contentBefore = fs.readFileSync(path.join(srcDir, 'env.d.ts'), 'utf-8');
if (!hasFrameworkShim(tmpDir, 'vue')) {
addFrameworkShim(tmpDir, 'vue');
}
const contentAfter = fs.readFileSync(path.join(srcDir, 'env.d.ts'), 'utf-8');
expect(contentAfter).toBe(contentBefore);
});

it('adds shim for vue project created without env.d.ts', () => {
fs.mkdirSync(path.join(tmpDir, 'src'));
fs.writeFileSync(
path.join(tmpDir, 'package.json'),
JSON.stringify({ devDependencies: { vue: '^3.0.0' } }),
);
const framework = detectFramework(tmpDir);
expect(framework).toBe('vue');
if (framework && !hasFrameworkShim(tmpDir, framework)) {
addFrameworkShim(tmpDir, framework);
}
const content = fs.readFileSync(path.join(tmpDir, 'src', 'env.d.ts'), 'utf-8');
expect(content).toContain("declare module '*.vue'");
});

it('adds astro shim for astro project without env.d.ts', () => {
fs.writeFileSync(
path.join(tmpDir, 'package.json'),
JSON.stringify({ devDependencies: { astro: '^4.0.0' } }),
);
const framework = detectFramework(tmpDir);
expect(framework).toBe('astro');
if (framework && !hasFrameworkShim(tmpDir, framework)) {
addFrameworkShim(tmpDir, framework);
}
const content = fs.readFileSync(path.join(tmpDir, 'env.d.ts'), 'utf-8');
expect(content).toContain('/// <reference types="astro/client" />');
});
});
});
Loading
Loading