mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-10 04:22:12 +00:00
[cli] Use detectFrameworks() during vc link --repo (#10203)
Allows for multiple frameworks to be detectable within the same root directory. This is basically specifically for Storybook. <img width="700" alt="Screenshot 2023-07-12 at 6 04 37 PM" src="https://github.com/vercel/vercel/assets/71256/5a240f1e-b000-42ad-b36f-3c151d3cd449">
This commit is contained in:
5
.changeset/weak-days-yawn.md
Normal file
5
.changeset/weak-days-yawn.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'vercel': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Detect multiple frameworks within the same root directory during `vc link --repo`
|
||||||
@@ -15,6 +15,7 @@ import { emoji, prependEmoji } from '../emoji';
|
|||||||
import selectOrg from '../input/select-org';
|
import selectOrg from '../input/select-org';
|
||||||
import { addToGitIgnore } from './add-to-gitignore';
|
import { addToGitIgnore } from './add-to-gitignore';
|
||||||
import type Client from '../client';
|
import type Client from '../client';
|
||||||
|
import type { Framework } from '@vercel/frameworks';
|
||||||
import type { Project } from '@vercel-internals/types';
|
import type { Project } from '@vercel-internals/types';
|
||||||
import createProject from '../projects/create-project';
|
import createProject from '../projects/create-project';
|
||||||
import { detectProjects } from '../projects/detect-projects';
|
import { detectProjects } from '../projects/detect-projects';
|
||||||
@@ -90,7 +91,7 @@ export async function ensureRepoLink(
|
|||||||
// they will be ready by the time the projects are listed
|
// they will be ready by the time the projects are listed
|
||||||
const detectedProjectsPromise = detectProjects(rootPath).catch(err => {
|
const detectedProjectsPromise = detectProjects(rootPath).catch(err => {
|
||||||
output.debug(`Failed to detect local projects: ${err}`);
|
output.debug(`Failed to detect local projects: ${err}`);
|
||||||
return new Map<string, string>();
|
return new Map<string, Framework[]>();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Not yet linked, so prompt user to begin linking
|
// Not yet linked, so prompt user to begin linking
|
||||||
@@ -187,17 +188,21 @@ export async function ensureRepoLink(
|
|||||||
detectedProjects.delete(project.rootDirectory ?? '');
|
detectedProjects.delete(project.rootDirectory ?? '');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (detectedProjects.size > 0) {
|
const detectedProjectsCount = Array.from(detectedProjects.values()).reduce(
|
||||||
|
(o, f) => o + f.length,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
if (detectedProjectsCount > 0) {
|
||||||
output.log(
|
output.log(
|
||||||
`Detected ${pluralize(
|
`Detected ${pluralize(
|
||||||
'new Project',
|
'new Project',
|
||||||
detectedProjects.size,
|
detectedProjectsCount,
|
||||||
true
|
true
|
||||||
)} that may be created.`
|
)} that may be created.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const addSeparators = projects.length > 0 && detectedProjects.size > 0;
|
const addSeparators = projects.length > 0 && detectedProjectsCount > 0;
|
||||||
const { selected } = await client.prompt({
|
const { selected } = await client.prompt({
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
name: 'selected',
|
name: 'selected',
|
||||||
@@ -218,23 +223,30 @@ export async function ensureRepoLink(
|
|||||||
...(addSeparators
|
...(addSeparators
|
||||||
? [new inquirer.Separator('----- New Projects to be created -----')]
|
? [new inquirer.Separator('----- New Projects to be created -----')]
|
||||||
: []),
|
: []),
|
||||||
...Array.from(detectedProjects.entries()).map(
|
...Array.from(detectedProjects.entries()).flatMap(
|
||||||
([rootDirectory, framework]) => {
|
([rootDirectory, frameworks]) =>
|
||||||
const name = slugify(
|
frameworks.map((framework, i) => {
|
||||||
[basename(rootPath), basename(rootDirectory)]
|
const name = slugify(
|
||||||
.filter(Boolean)
|
[
|
||||||
.join('-')
|
basename(rootPath),
|
||||||
);
|
basename(rootDirectory),
|
||||||
return {
|
i > 0 ? framework.slug : '',
|
||||||
name: `${org.slug}/${name} (${framework})`,
|
]
|
||||||
value: {
|
.filter(Boolean)
|
||||||
newProject: true,
|
.join('-')
|
||||||
rootDirectory,
|
);
|
||||||
name,
|
return {
|
||||||
framework,
|
name: `${org.slug}/${name} (${framework.name})`,
|
||||||
},
|
value: {
|
||||||
};
|
newProject: true,
|
||||||
}
|
rootDirectory,
|
||||||
|
name,
|
||||||
|
framework,
|
||||||
|
},
|
||||||
|
// Checked by default when there are no other existing Projects
|
||||||
|
checked: projects.length === 0,
|
||||||
|
};
|
||||||
|
})
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@@ -251,7 +263,10 @@ export async function ensureRepoLink(
|
|||||||
output.spinner(`Creating new Project: ${orgAndName}`);
|
output.spinner(`Creating new Project: ${orgAndName}`);
|
||||||
delete selection.newProject;
|
delete selection.newProject;
|
||||||
if (!selection.rootDirectory) delete selection.rootDirectory;
|
if (!selection.rootDirectory) delete selection.rootDirectory;
|
||||||
selected[i] = await createProject(client, selection);
|
selected[i] = await createProject(client, {
|
||||||
|
...selection,
|
||||||
|
framework: selection.framework.slug,
|
||||||
|
});
|
||||||
await connectGitProvider(
|
await connectGitProvider(
|
||||||
client,
|
client,
|
||||||
org,
|
org,
|
||||||
@@ -262,7 +277,8 @@ export async function ensureRepoLink(
|
|||||||
output.log(
|
output.log(
|
||||||
`Created new Project: ${output.link(
|
`Created new Project: ${output.link(
|
||||||
orgAndName,
|
orgAndName,
|
||||||
`https://vercel.com/${orgAndName}`
|
`https://vercel.com/${orgAndName}`,
|
||||||
|
{ fallback: false }
|
||||||
)}`
|
)}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -287,9 +303,11 @@ export async function ensureRepoLink(
|
|||||||
|
|
||||||
output.print(
|
output.print(
|
||||||
prependEmoji(
|
prependEmoji(
|
||||||
`Linked to ${repoUrlLink} under ${chalk.bold(
|
`Linked to ${pluralize(
|
||||||
org.slug
|
'Project',
|
||||||
)} (created ${VERCEL_DIR}${
|
selected.length,
|
||||||
|
true
|
||||||
|
)} under ${chalk.bold(org.slug)} (created ${VERCEL_DIR}${
|
||||||
isGitIgnoreUpdated ? ' and added it to .gitignore' : ''
|
isGitIgnoreUpdated ? ' and added it to .gitignore' : ''
|
||||||
})`,
|
})`,
|
||||||
emoji('link')
|
emoji('link')
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import frameworks from '@vercel/frameworks';
|
import frameworkList, { Framework } from '@vercel/frameworks';
|
||||||
import {
|
import {
|
||||||
detectFramework,
|
detectFrameworks,
|
||||||
getWorkspacePackagePaths,
|
getWorkspacePackagePaths,
|
||||||
getWorkspaces,
|
getWorkspaces,
|
||||||
LocalFileSystemDetector,
|
LocalFileSystemDetector,
|
||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
export async function detectProjects(cwd: string) {
|
export async function detectProjects(cwd: string) {
|
||||||
const fs = new LocalFileSystemDetector(cwd);
|
const fs = new LocalFileSystemDetector(cwd);
|
||||||
const workspaces = await getWorkspaces({ fs });
|
const workspaces = await getWorkspaces({ fs });
|
||||||
const detectedProjects = new Map<string, string>();
|
const detectedProjects = new Map<string, Framework[]>();
|
||||||
const packagePaths = (
|
const packagePaths = (
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
workspaces.map(workspace =>
|
workspaces.map(workspace =>
|
||||||
@@ -26,12 +26,12 @@ export async function detectProjects(cwd: string) {
|
|||||||
}
|
}
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
packagePaths.map(async p => {
|
packagePaths.map(async p => {
|
||||||
const framework = await detectFramework({
|
const frameworks = await detectFrameworks({
|
||||||
fs: fs.chdir(join('.', p)),
|
fs: fs.chdir(join('.', p)),
|
||||||
frameworkList: frameworks,
|
frameworkList,
|
||||||
});
|
});
|
||||||
if (!framework) return;
|
if (frameworks.length === 0) return;
|
||||||
detectedProjects.set(p.slice(1), framework);
|
detectedProjects.set(p.slice(1), frameworks);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
return detectedProjects;
|
return detectedProjects;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
import type { Framework } from '@vercel/frameworks';
|
||||||
import { detectProjects } from '../../../../src/util/projects/detect-projects';
|
import { detectProjects } from '../../../../src/util/projects/detect-projects';
|
||||||
|
|
||||||
const REPO_ROOT = join(__dirname, '../../../../../..');
|
const REPO_ROOT = join(__dirname, '../../../../../..');
|
||||||
@@ -8,23 +9,35 @@ const FS_DETECTORS_FIXTURES = join(
|
|||||||
'packages/fs-detectors/test/fixtures'
|
'packages/fs-detectors/test/fixtures'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
function mapDetected(
|
||||||
|
detected: Map<string, Framework[]>
|
||||||
|
): Array<[string, string[]]> {
|
||||||
|
return [...detected.entries()]
|
||||||
|
.sort((a, b) => a[0].localeCompare(b[0]))
|
||||||
|
.map(([dir, frameworks]) => [dir, frameworks.map(f => f.slug as string)]);
|
||||||
|
}
|
||||||
|
|
||||||
describe('detectProjects()', () => {
|
describe('detectProjects()', () => {
|
||||||
it('should match "nextjs" example', async () => {
|
it('should match 1 Project in "nextjs" example', async () => {
|
||||||
const dir = join(EXAMPLES_DIR, 'nextjs');
|
const dir = join(EXAMPLES_DIR, 'nextjs');
|
||||||
const detected = await detectProjects(dir);
|
const detected = await detectProjects(dir);
|
||||||
expect([...detected.entries()]).toEqual([['', 'nextjs']]);
|
expect(mapDetected(detected)).toEqual([['', ['nextjs']]]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should match 2 Projects in "storybook" example', async () => {
|
||||||
|
const dir = join(EXAMPLES_DIR, 'storybook');
|
||||||
|
const detected = await detectProjects(dir);
|
||||||
|
expect(mapDetected(detected)).toEqual([['', ['nextjs', 'storybook']]]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should match "30-double-nested-workspaces"', async () => {
|
it('should match "30-double-nested-workspaces"', async () => {
|
||||||
const dir = join(FS_DETECTORS_FIXTURES, '30-double-nested-workspaces');
|
const dir = join(FS_DETECTORS_FIXTURES, '30-double-nested-workspaces');
|
||||||
const detected = await detectProjects(dir);
|
const detected = await detectProjects(dir);
|
||||||
expect(
|
expect(mapDetected(detected)).toEqual([
|
||||||
[...detected.entries()].sort((a, b) => a[0].localeCompare(b[0]))
|
['packages/backend/c', ['remix']],
|
||||||
).toEqual([
|
['packages/backend/d', ['nextjs']],
|
||||||
['packages/backend/c', 'remix'],
|
['packages/frontend/a', ['hexo']],
|
||||||
['packages/backend/d', 'nextjs'],
|
['packages/frontend/b', ['ember']],
|
||||||
['packages/frontend/a', 'hexo'],
|
|
||||||
['packages/frontend/b', 'ember'],
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user