diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index aa284d05..23e5af7a 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -11,12 +11,12 @@ jobs: strategy: matrix: os: [ubuntu-latest, macOS-latest, windows-latest] - node-version: ['12.20.0', '*'] + node-version: ['14.14.0', '*'] exclude: - os: macOS-latest - node-version: '12.20.0' + node-version: '14.14.0' - os: windows-latest - node-version: '12.20.0' + node-version: '14.14.0' fail-fast: false runs-on: ${{ matrix.os }} steps: diff --git a/src/core.js b/src/core.js index b9588622..36d44bcf 100644 --- a/src/core.js +++ b/src/core.js @@ -67,12 +67,14 @@ const getContext = (context) => { */ export const listFrameworks = async function (context) { const { pathExists, packageJson, packageJsonPath, nodeVersion } = getContext(context) - const { npmDependencies, scripts, runScriptCommand } = await getProjectInfo({ + const { npmDependencies, npmDependenciesVersions, scripts, runScriptCommand } = await getProjectInfo({ pathExists, packageJson, packageJsonPath, }) - const frameworks = await pFilter(FRAMEWORKS, (framework) => usesFramework(framework, { pathExists, npmDependencies })) + const frameworks = await pFilter(FRAMEWORKS, (framework) => + usesFramework(framework, { pathExists, npmDependencies, npmDependenciesVersions }), + ) const frameworkInfos = frameworks.map((framework) => getFrameworkInfo(framework, { scripts, runScriptCommand, nodeVersion }), ) @@ -90,8 +92,12 @@ export const listFrameworks = async function (context) { export const hasFramework = async function (frameworkId, context) { const framework = getFrameworkById(frameworkId) const { pathExists, packageJson, packageJsonPath } = getContext(context) - const { npmDependencies } = await getProjectInfo({ pathExists, packageJson, packageJsonPath }) - const result = await usesFramework(framework, { pathExists, npmDependencies }) + const { npmDependencies, npmDependenciesVersions } = await getProjectInfo({ + pathExists, + packageJson, + packageJsonPath, + }) + const result = await usesFramework(framework, { pathExists, npmDependencies, npmDependenciesVersions }) return result } @@ -131,11 +137,12 @@ const getFrameworkId = function ({ id }) { } const getProjectInfo = async function ({ pathExists, packageJson, packageJsonPath }) { - const { npmDependencies, scripts } = await getPackageJsonContent({ + const { npmDependencies, npmDependenciesVersions, scripts } = getPackageJsonContent({ packageJson, }) const runScriptCommand = await getRunScriptCommand({ pathExists, packageJsonPath }) - return { npmDependencies, scripts, runScriptCommand } + + return { npmDependencies, npmDependenciesVersions, scripts, runScriptCommand } } const getFrameworkInfo = function ( @@ -155,11 +162,12 @@ const getFrameworkInfo = function ( ) { const devCommands = getDevCommands({ frameworkDevCommand, scripts, runScriptCommand }) const recommendedPlugins = getPlugins(plugins, { nodeVersion }) + return { id, name, package: { - name: detect.npmDependencies[0], + name: typeof detect.npmDependencies[0] === 'string' ? detect.npmDependencies[0] : detect.npmDependencies[0]?.name, version: 'unknown', }, category, diff --git a/src/detect.js b/src/detect.js index 7d5230c0..89999e38 100644 --- a/src/detect.js +++ b/src/detect.js @@ -1,4 +1,5 @@ import pLocate from 'p-locate' +import semver from 'semver' // Checks if the project is using a specific framework: // - if `framework.npmDependencies` is set, one of them must be present in the @@ -14,20 +15,34 @@ export const usesFramework = async function ( configFiles, }, }, - { pathExists, npmDependencies }, + { pathExists, npmDependencies, npmDependenciesVersions }, ) { return ( - usesNpmDependencies(frameworkNpmDependencies, npmDependencies) && + usesNpmDependencies(frameworkNpmDependencies, npmDependencies, npmDependenciesVersions) && lacksExcludedNpmDependencies(frameworkExcludedNpmDependencies, npmDependencies) && (await usesConfigFiles(configFiles, pathExists)) ) } -const usesNpmDependencies = function (frameworkNpmDependencies, npmDependencies) { - return ( - frameworkNpmDependencies.length === 0 || - frameworkNpmDependencies.some((frameworkNpmDependency) => npmDependencies.includes(frameworkNpmDependency)) - ) +const usesNpmDependencies = function (frameworkNpmDependencies, npmDependencies, npmDependenciesVersions) { + if (frameworkNpmDependencies.length === 0) { + return true + } + + return frameworkNpmDependencies.some((frameworkNpmDependency) => { + if (typeof frameworkNpmDependency === 'string') { + return npmDependencies.includes(frameworkNpmDependency) + } + + return ( + npmDependencies.includes(frameworkNpmDependency.name) && + (frameworkNpmDependency.version == null || + semver.satisfies( + semver.minVersion(npmDependenciesVersions[frameworkNpmDependency.name]), + frameworkNpmDependency.version, + )) + ) + }) } const lacksExcludedNpmDependencies = function (frameworkExcludedNpmDependencies, npmDependencies) { diff --git a/src/frameworks/gatsby.json b/src/frameworks/gatsby.json index 7087522f..a422a1c2 100644 --- a/src/frameworks/gatsby.json +++ b/src/frameworks/gatsby.json @@ -3,7 +3,7 @@ "name": "Gatsby", "category": "static_site_generator", "detect": { - "npmDependencies": ["gatsby"], + "npmDependencies": [{ "name": "gatsby", "version": "<5.0.0" }], "excludedNpmDependencies": [], "configFiles": ["gatsby-config.js"] }, diff --git a/src/frameworks/gatsby5.json b/src/frameworks/gatsby5.json new file mode 100644 index 00000000..4d650b24 --- /dev/null +++ b/src/frameworks/gatsby5.json @@ -0,0 +1,37 @@ +{ + "id": "gatsby5", + "name": "Gatsby 5", + "category": "static_site_generator", + "detect": { + "npmDependencies": [{ "name": "gatsby", "version": ">=5.0.0" }], + "excludedNpmDependencies": [], + "configFiles": ["gatsby-config.js"] + }, + "dev": { + "command": "gatsby develop", + "port": 8000, + "pollingStrategies": [{ "name": "TCP" }, { "name": "HTTP" }] + }, + "build": { + "command": "gatsby build", + "directory": "public" + }, + "staticAssetsDirectory": "static", + "env": { + "GATSBY_LOGGER": "yurnalist", + "GATSBY_PRECOMPILE_DEVELOP_FUNCTIONS": "true", + "AWS_LAMBDA_JS_RUNTIME": "nodejs18.x", + "NODE_VERSION": "18" + }, + "logo": { + "default": "/logos/gatsby/default.svg", + "light": "/logos/gatsby/light.svg", + "dark": "/logos/gatsby/dark.svg" + }, + "plugins": [ + { + "packageName": "@netlify/plugin-gatsby", + "condition": { "minNodeVersion": "12.13.0" } + } + ] +} diff --git a/src/frameworks/main.js b/src/frameworks/main.js index da149f6e..55b153b3 100644 --- a/src/frameworks/main.js +++ b/src/frameworks/main.js @@ -8,6 +8,7 @@ export const FRAMEWORK_NAMES = [ 'docusaurus-v2', 'eleventy', 'gatsby', + 'gatsby5', 'gridsome', 'hexo', 'hugo', diff --git a/src/package.js b/src/package.js index d183b051..f7076241 100644 --- a/src/package.js +++ b/src/package.js @@ -7,8 +7,9 @@ export const getPackageJsonContent = function ({ packageJson }) { } const npmDependencies = getNpmDependencies(packageJson) + const npmDependenciesVersions = getNpmDependenciesVersions(packageJson) const scripts = getScripts(packageJson) - return { npmDependencies, scripts } + return { npmDependencies, npmDependenciesVersions, scripts } } // Retrieve `package.json` `dependencies` and `devDependencies` names @@ -16,6 +17,11 @@ const getNpmDependencies = function ({ dependencies, devDependencies }) { return [...getObjectKeys(dependencies), ...getObjectKeys(devDependencies)] } +// Retrieve `package.json` `dependencies` and `devDependencies` versions +const getNpmDependenciesVersions = function ({ dependencies, devDependencies }) { + return { ...devDependencies, ...dependencies } +} + const getObjectKeys = function (value) { if (!isPlainObj(value)) { return [] diff --git a/test/detect.js b/test/detect.js index 8f5ba72e..17e22db1 100644 --- a/test/detect.js +++ b/test/detect.js @@ -49,3 +49,15 @@ if (nodeVersion === 'v8.3.0') { t.is(frameworks[0].plugins.length, 0) }) } + +test('Should detect specific version of dependencies', async (t) => { + const frameworks = await getFrameworks('gatsby5') + t.is(frameworks.length, 1) + t.is(frameworks[0].id, 'gatsby5') +}) + +test('Should not detect specific version of dependencies if version does not match', async (t) => { + const frameworks = await getFrameworks('gatsby4') + t.is(frameworks.length, 1) + t.is(frameworks[0].id, 'gatsby') +}) diff --git a/test/fixtures/gatsby4/gatsby-config.js b/test/fixtures/gatsby4/gatsby-config.js new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/gatsby4/package.json b/test/fixtures/gatsby4/package.json new file mode 100644 index 00000000..863481d2 --- /dev/null +++ b/test/fixtures/gatsby4/package.json @@ -0,0 +1,7 @@ +{ + "name": "test", + "version": "1.0.0", + "dependencies": { + "gatsby": "4" + } +} diff --git a/test/fixtures/gatsby5/gatsby-config.js b/test/fixtures/gatsby5/gatsby-config.js new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/gatsby5/package.json b/test/fixtures/gatsby5/package.json new file mode 100644 index 00000000..56a5f649 --- /dev/null +++ b/test/fixtures/gatsby5/package.json @@ -0,0 +1,7 @@ +{ + "name": "test", + "version": "1.0.0", + "dependencies": { + "gatsby": "5" + } +} diff --git a/test/frameworks.js b/test/frameworks.js index 7bc1c2b3..4021c766 100644 --- a/test/frameworks.js +++ b/test/frameworks.js @@ -70,7 +70,17 @@ const FRAMEWORK_JSON_SCHEMA = { properties: { npmDependencies: { type: 'array', - items: { type: 'string', minLength: 1 }, + items: { + anyOf: [ + { type: 'string', minLength: 1 }, + { + type: 'object', + required: ['name'], + additionalProperties: false, + properties: { name: { type: 'string', minLength: 1 }, version: { type: 'string', minLength: 1 } }, + }, + ], + }, }, excludedNpmDependencies: { type: 'array',