feat: support version matcher functions for framework detection

This commit is contained in:
Blaine Bublitz 2021-01-26 16:50:07 -07:00
parent 499de3f465
commit 231b03aa9a
3 changed files with 77 additions and 11 deletions

View File

@ -315,6 +315,27 @@ describe('Detect', () => {
expect(result).toBe(ProjectType.UNDETECTED);
});
// TODO(blaine): Remove once Vue3 is supported
it(`UNDETECTED for Vue framework above version 3.0.0`, () => {
const result = detectFrameworkPreset({
dependencies: {
vue: '3.0.0',
},
});
expect(result).toBe(ProjectType.UNDETECTED);
});
// TODO(blaine): Remove once Nuxt3 is supported
it(`UNDETECTED for Nuxt framework above version 3.0.0`, () => {
const result = detectFrameworkPreset({
dependencies: {
nuxt: '3.0.0',
},
});
expect(result).toBe(ProjectType.UNDETECTED);
});
// TODO: The mocking in this test causes tests after it to fail
it('REACT_SCRIPTS for custom react scripts config', () => {
const forkedReactScriptsConfig = {
'/node_modules/.bin/react-scripts': 'file content',

View File

@ -12,14 +12,32 @@ import {
import { getBowerJson } from './helpers';
import { PackageJson, readPackageJson } from './js-package-manager';
const hasDependency = (packageJson: PackageJson, name: string) => {
return !!packageJson.dependencies?.[name] || !!packageJson.devDependencies?.[name];
const hasDependency = (
packageJson: PackageJson,
name: string,
matcher?: (version: string) => boolean
) => {
const version = packageJson.dependencies?.[name] || packageJson.devDependencies?.[name];
if (version && typeof matcher === 'function') {
return matcher(version);
}
return !!version;
};
const hasPeerDependency = (packageJson: PackageJson, name: string) => {
return !!packageJson.peerDependencies?.[name];
const hasPeerDependency = (
packageJson: PackageJson,
name: string,
matcher?: (version: string) => boolean
) => {
const version = packageJson.peerDependencies?.[name];
if (version && typeof matcher === 'function') {
return matcher(version);
}
return !!version;
};
type SearchTuple = [string, (version: string) => boolean | undefined];
const getFrameworkPreset = (
packageJson: PackageJson,
framework: TemplateConfiguration
@ -32,12 +50,32 @@ const getFrameworkPreset = (
const { preset, files, dependencies, peerDependencies, matcherFunction } = framework;
if (Array.isArray(dependencies) && dependencies.length > 0) {
matcher.dependencies = dependencies.map((name) => hasDependency(packageJson, name));
let dependencySearches = [] as SearchTuple[];
if (Array.isArray(dependencies)) {
dependencySearches = dependencies.map((name) => [name, undefined]);
} else if (typeof dependencies === 'object') {
dependencySearches = Object.entries(dependencies);
}
if (Array.isArray(peerDependencies) && peerDependencies.length > 0) {
matcher.peerDependencies = peerDependencies.map((name) => hasPeerDependency(packageJson, name));
// Must check the length so the `[false]` isn't overwritten if `{ dependencies: [] }`
if (dependencySearches.length > 0) {
matcher.dependencies = dependencySearches.map(([name, matchFn]) =>
hasDependency(packageJson, name, matchFn)
);
}
let peerDependencySearches = [] as SearchTuple[];
if (Array.isArray(peerDependencies)) {
peerDependencySearches = peerDependencies.map((name) => [name, undefined]);
} else if (typeof peerDependencies === 'object') {
peerDependencySearches = Object.entries(peerDependencies);
}
// Must check the length so the `[false]` isn't overwritten if `{ peerDependencies: [] }`
if (peerDependencySearches.length > 0) {
matcher.peerDependencies = peerDependencySearches.map(([name, matchFn]) =>
hasPeerDependency(packageJson, name, matchFn)
);
}
if (Array.isArray(files) && files.length > 0) {

View File

@ -1,3 +1,5 @@
import { lt } from '@storybook/semver';
// Should match @storybook/<framework>
export type SupportedFrameworks =
| 'react'
@ -82,8 +84,8 @@ export type TemplateMatcher = {
export type TemplateConfiguration = {
preset: ProjectType;
/** will be checked both against dependencies and devDependencies */
dependencies?: string[];
peerDependencies?: string[];
dependencies?: string[] | { [dependency: string]: (version: string) => boolean };
peerDependencies?: string[] | { [dependency: string]: (version: string) => boolean };
files?: string[];
matcherFunction: (matcher: TemplateMatcher) => boolean;
};
@ -112,7 +114,12 @@ export const supportedTemplates: TemplateConfiguration[] = [
},
{
preset: ProjectType.VUE,
dependencies: ['vue', 'nuxt'],
// The Vue template only works with Vue or Nuxt under v3
// In a future update, a new Vue3 template will be added
dependencies: {
vue: (version) => lt(version, '3.0.0'),
nuxt: (version) => lt(version, '3.0.0'),
},
matcherFunction: ({ dependencies }) => {
return dependencies.some(Boolean);
},