From bbe6513e0ea306a0a0cb389242d0a6254b38fb9c Mon Sep 17 00:00:00 2001 From: joao10lima Date: Mon, 3 Jan 2022 17:02:11 -0300 Subject: [PATCH] chore(plugin): add dependencies list to context As explained on the issue https://github.com/pipelinit/pipelinit-cli/issues/124 This commit adds the projects dependencies list on the plugin context Before: ```typescript import { hasRubyDependency } from "./dependencies.ts"; const isDependency = await hasRubyDependency(context, "rubocop"); ``` At every import and function call the application would go on the dependencies files(package.json, Pipfile, etc ...) and search for the requested dependencies. This made the application very slow in addition to harming the organization of the code with many unnecessary imports. After: ```typescript const isDependency = await context.dependencies.includes("rubocop"); ``` Now the dependency list is loaded before the introspection and only need to read the necessary dependencies files once. Resolves: https://github.com/pipelinit/pipelinit-cli/issues/124 --- .../lib/context/dependencies/javascript.ts | 43 ++ cli/src/lib/context/dependencies/mod.ts | 3 + .../src/lib/context/dependencies/python.ts | 20 +- .../src/lib/context/dependencies/ruby.ts | 20 +- cli/src/lib/context/depsIntrospect.ts | 27 ++ cli/src/lib/context/mod.ts | 1 + cli/src/lib/introspect.ts | 6 + .../stack/javascript/dependencies.test.ts | 103 ----- core/plugins/stack/javascript/dependencies.ts | 77 ---- core/plugins/stack/javascript/mod.test.ts | 121 ------ core/plugins/stack/javascript/type.ts | 6 +- core/plugins/stack/mod.ts | 31 +- core/plugins/stack/python/bandit.ts | 3 +- core/plugins/stack/python/black.ts | 3 +- core/plugins/stack/python/django.ts | 3 +- core/plugins/stack/python/flake8.ts | 3 +- core/plugins/stack/python/isort.ts | 3 +- core/plugins/stack/python/mod.test.ts | 381 ------------------ core/plugins/stack/python/pylint.ts | 3 +- core/plugins/stack/python/pytest.ts | 3 +- core/plugins/stack/python/type.ts | 3 +- core/plugins/stack/ruby/rubocop.ts | 3 +- core/plugins/stack/ruby/types.ts | 3 +- core/tests/mod.ts | 1 + core/types.ts | 1 + 25 files changed, 120 insertions(+), 751 deletions(-) create mode 100644 cli/src/lib/context/dependencies/javascript.ts create mode 100644 cli/src/lib/context/dependencies/mod.ts rename core/plugins/stack/python/dependencies.ts => cli/src/lib/context/dependencies/python.ts (70%) rename core/plugins/stack/ruby/dependencies.ts => cli/src/lib/context/dependencies/ruby.ts (50%) create mode 100644 cli/src/lib/context/depsIntrospect.ts delete mode 100644 core/plugins/stack/javascript/dependencies.test.ts delete mode 100644 core/plugins/stack/javascript/dependencies.ts delete mode 100644 core/plugins/stack/javascript/mod.test.ts delete mode 100644 core/plugins/stack/python/mod.test.ts diff --git a/cli/src/lib/context/dependencies/javascript.ts b/cli/src/lib/context/dependencies/javascript.ts new file mode 100644 index 0000000..ef280f8 --- /dev/null +++ b/cli/src/lib/context/dependencies/javascript.ts @@ -0,0 +1,43 @@ +import { Context } from "../../../../deps.ts"; + +const denoDepRegex = /\/\/.*\/(?.+?)(?=\/|@)/gm; + +function notEmpty(value: TValue | null | undefined): value is TValue { + return value !== null && value !== undefined; +} + +export const jsNodeDependency = async (context: Context) => { + const dependencies: string[] = []; + + for await (const file of context.files.each("**/package.json")) { + const packageJson = await context.files.readJSON(file.path); + + const nodeDeps = packageJson?.dependencies; + if (nodeDeps) { + dependencies.push(...Object.keys(nodeDeps)); + } + + const nodeDepsDev = packageJson?.devDependencies; + if (nodeDepsDev) { + dependencies.push(...Object.keys(nodeDepsDev)); + } + } + + return dependencies; +}; + +export const jsDenoDependency = async (context: Context) => { + for await (const file of await context.files.each("**/deps.ts")) { + const denoDepsText = await context.files.readText(file.path); + + const depsDeno: string[] = Array.from( + denoDepsText.matchAll(denoDepRegex), + (match) => !match.groups ? null : match.groups.DependencyName, + ).filter(notEmpty); + + if (depsDeno) { + return depsDeno; + } + } + return []; +}; diff --git a/cli/src/lib/context/dependencies/mod.ts b/cli/src/lib/context/dependencies/mod.ts new file mode 100644 index 0000000..6b0a282 --- /dev/null +++ b/cli/src/lib/context/dependencies/mod.ts @@ -0,0 +1,3 @@ +export * from "./javascript.ts"; +export * from "./python.ts"; +export * from "./ruby.ts"; diff --git a/core/plugins/stack/python/dependencies.ts b/cli/src/lib/context/dependencies/python.ts similarity index 70% rename from core/plugins/stack/python/dependencies.ts rename to cli/src/lib/context/dependencies/python.ts index 8504f31..90dfbc8 100644 --- a/core/plugins/stack/python/dependencies.ts +++ b/cli/src/lib/context/dependencies/python.ts @@ -1,6 +1,6 @@ -import { Context } from "../../../types.ts"; +import { Context } from "../../../../deps.ts"; -const readDependencyFile = async (context: Context) => { +export const pythonDependency = async (context: Context) => { const dependencies: string[] = []; for await (const file of context.files.each("./pyproject.toml")) { @@ -42,19 +42,3 @@ const readDependencyFile = async (context: Context) => { return dependencies; }; - -export const hasPythonDependency = async ( - context: Context, - dependencyName: string, -): Promise => { - const dependencies = await readDependencyFile(context); - return dependencies.some((dep) => dep === dependencyName); -}; - -export const hasPythonDependencyAny = async ( - context: Context, - dependencyList: Set, -): Promise => { - const dependencies = await readDependencyFile(context); - return dependencies.some((dep) => dependencyList.has(dep)); -}; diff --git a/core/plugins/stack/ruby/dependencies.ts b/cli/src/lib/context/dependencies/ruby.ts similarity index 50% rename from core/plugins/stack/ruby/dependencies.ts rename to cli/src/lib/context/dependencies/ruby.ts index 24fe1fd..067b500 100644 --- a/core/plugins/stack/ruby/dependencies.ts +++ b/cli/src/lib/context/dependencies/ruby.ts @@ -1,4 +1,4 @@ -import { Context } from "../../../types.ts"; +import { Context } from "../../../../deps.ts"; const rubyDepRegex = /(?:(["'])(?[a-zA-Z\-_\.]+)["'])/gm; @@ -6,7 +6,7 @@ function notEmpty(value: TValue | null | undefined): value is TValue { return value !== null && value !== undefined; } -const readDependencyFile = async (context: Context) => { +export const rubyDependency = async (context: Context) => { for await (const file of await context.files.each("**/Gemfile")) { const rubyDepsText = await context.files.readText(file.path); @@ -21,19 +21,3 @@ const readDependencyFile = async (context: Context) => { } return []; }; - -export const hasRubyDependency = async ( - context: Context, - dependencyName: string, -): Promise => { - const dependencies = await readDependencyFile(context); - return dependencies.some((dep) => dep === dependencyName); -}; - -export const hasRubyDependencyAny = async ( - context: Context, - dependencyList: Set, -): Promise => { - const dependencies = await readDependencyFile(context); - return dependencies.some((dep) => dependencyList.has(dep)); -}; diff --git a/cli/src/lib/context/depsIntrospect.ts b/cli/src/lib/context/depsIntrospect.ts new file mode 100644 index 0000000..3227cea --- /dev/null +++ b/cli/src/lib/context/depsIntrospect.ts @@ -0,0 +1,27 @@ +import { Context, introspectors } from "../../../deps.ts"; +import { + jsDenoDependency, + jsNodeDependency, + pythonDependency, + rubyDependency, +} from "./dependencies/mod.ts"; + +type StacksList = ((typeof introspectors)[number]["name"])[]; + +export async function listDependencies( + stackList: StacksList, + context: Context, +): Promise { + for (const stack of stackList) { + switch (stack) { + case "javascript": + return await jsNodeDependency(context) || + await jsDenoDependency(context); + case "python": + return await pythonDependency(context); + case "ruby": + return await rubyDependency(context); + } + } + return []; +} diff --git a/cli/src/lib/context/mod.ts b/cli/src/lib/context/mod.ts index abdb9fb..d146089 100644 --- a/cli/src/lib/context/mod.ts +++ b/cli/src/lib/context/mod.ts @@ -28,6 +28,7 @@ export const context: Context = { suggestDefault: true, strict: true, version: PIPELINIT_VERSION, + dependencies: [], }; /** diff --git a/cli/src/lib/introspect.ts b/cli/src/lib/introspect.ts index f477197..da55702 100644 --- a/cli/src/lib/introspect.ts +++ b/cli/src/lib/introspect.ts @@ -1,5 +1,6 @@ import { introspectors, log, ProjectData } from "../../deps.ts"; import { context } from "./context/mod.ts"; +import { listDependencies } from "./context/depsIntrospect.ts"; export type Stack = Record; @@ -45,6 +46,11 @@ export async function introspect() { logger.info(`Detected stack: ${stackNames}`); } + context.dependencies = await listDependencies( + stack.map((t) => t.name), + context, + ); + const introspected = (await Promise.all( stack.map>>((introspector) => introspector.introspect(context) diff --git a/core/plugins/stack/javascript/dependencies.test.ts b/core/plugins/stack/javascript/dependencies.test.ts deleted file mode 100644 index 9b63715..0000000 --- a/core/plugins/stack/javascript/dependencies.test.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { context } from "../../../tests/mod.ts"; -import { assertEquals, deepMerge } from "../../../deps.ts"; -import { FileEntry } from "../../../types.ts"; - -import { introspect } from "./type.ts"; - -const fakeContext = ( - { - isWebApp = false, - isNode = false, - isDeno = false, - } = {}, -) => { - return deepMerge( - context, - { - files: { - each: async function* (glob: string): AsyncIterableIterator { - if (glob === "**/*.{html,vue}") { - yield { - name: "index.ts", - path: "fake-path", - }; - yield { - name: "index.js", - path: "fake-path", - }; - } - if (glob === "**/package.json" && isNode) { - yield { - name: "package.json", - path: "fake-path", - }; - } - if (glob === "**/deps.ts" && isDeno) { - yield { - name: "deps.ts", - path: "fake-path-deps", - }; - } - return; - }, - // deno-lint-ignore require-await - readJSON: async (path: string): Promise> => { - const devDeps = { stylelint: "1.0.0", eslint: "7.2.2" }; - let dependencies = {}; - if (isWebApp) { - dependencies = { "vue": "3.0.0" }; - } - if (path === "fake-path") { - return { - dependencies: dependencies, - devDependencies: devDeps, - }; - } - return {}; - }, - // deno-lint-ignore require-await - readText: async (path: string) => { - if (path === "fake-path-deps") { - return `export { deepMerge } from "https://deno.land/std@0.104.0/collections/mod.ts"` + - (isWebApp - ? `\nexport { Application, Router } from "https://deno.land/x/oak/mod.ts";` - : ""); - } - return ""; - }, - }, - }, - ); -}; - -Deno.test("Plugins > Check if Type is identified correctly as webApp DENO", async () => { - const result = await introspect( - fakeContext({ isWebApp: true, isDeno: true }), - ); - - assertEquals(result, "webApp"); -}); - -Deno.test("Plugins > Check if Type is not identified DENO", async () => { - const result = await introspect( - fakeContext({ isWebApp: false, isDeno: true }), - ); - - assertEquals(result, null); -}); - -Deno.test("Plugins > Check if Type is identified correctly as webApp NODE", async () => { - const result = await introspect( - fakeContext({ isWebApp: true, isNode: true }), - ); - - assertEquals(result, "webApp"); -}); - -Deno.test("Plugins > Check if Type is not identified NODE", async () => { - const result = await introspect( - fakeContext({ isWebApp: false, isNode: true }), - ); - - assertEquals(result, null); -}); diff --git a/core/plugins/stack/javascript/dependencies.ts b/core/plugins/stack/javascript/dependencies.ts deleted file mode 100644 index ab55b25..0000000 --- a/core/plugins/stack/javascript/dependencies.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { Context } from "../../../types.ts"; - -const denoDepRegex = /\/\/.*\/(?.+?)(?=\/|@)/gm; - -function notEmpty(value: TValue | null | undefined): value is TValue { - return value !== null && value !== undefined; -} - -const readNodeDependencyFile = async (context: Context) => { - const dependencies: string[] = []; - - for await (const file of context.files.each("**/package.json")) { - const packageJson = await context.files.readJSON(file.path); - - const nodeDeps = packageJson?.dependencies; - if (nodeDeps) { - dependencies.push(...Object.keys(nodeDeps)); - } - - const nodeDepsDev = packageJson?.devDependencies; - if (nodeDepsDev) { - dependencies.push(...Object.keys(nodeDepsDev)); - } - } - - return dependencies; -}; - -const readDenoDependencyFile = async (context: Context) => { - for await (const file of await context.files.each("**/deps.ts")) { - const denoDepsText = await context.files.readText(file.path); - - const depsDeno: string[] = Array.from( - denoDepsText.matchAll(denoDepRegex), - (match) => !match.groups ? null : match.groups.DependencyName, - ).filter(notEmpty); - - if (depsDeno) { - return depsDeno; - } - } - return []; -}; - -export const hasDependency = async ( - context: Context, - dependencyName: string, -): Promise => { - const nodeDependencies = await readNodeDependencyFile(context); - if (nodeDependencies.length > 0) { - return nodeDependencies.some((dep) => dep === dependencyName); - } - - const denoDependencies = await readDenoDependencyFile(context); - if (denoDependencies.length > 0) { - return denoDependencies.some((dep) => dep === dependencyName); - } - - return false; -}; - -export const hasDependencyAny = async ( - context: Context, - dependencyList: Set, -): Promise => { - const nodeDependencies = await readNodeDependencyFile(context); - if (nodeDependencies.length > 0) { - return nodeDependencies.some((dep) => dependencyList.has(dep)); - } - - const denoDependencies = await readDenoDependencyFile(context); - if (denoDependencies.length > 0) { - return denoDependencies.some((dep) => dependencyList.has(dep)); - } - - return false; -}; diff --git a/core/plugins/stack/javascript/mod.test.ts b/core/plugins/stack/javascript/mod.test.ts deleted file mode 100644 index 1d9a22a..0000000 --- a/core/plugins/stack/javascript/mod.test.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { context } from "../../../tests/mod.ts"; -import { assertEquals, deepMerge } from "../../../deps.ts"; -import { FileEntry } from "../../../types.ts"; - -import { introspector } from "./mod.ts"; - -const fakeContext = ( - { - hasTestCommand = true, - } = {}, -) => { - return deepMerge( - context, - { - files: { - // deno-lint-ignore require-await - includes: async (glob: string): Promise => { - if (glob === "**/.eslintrc.{js,cjs,yaml,yml,json}") { - return true; - } - return false; - }, - each: async function* (glob: string): AsyncIterableIterator { - if (glob === "**/*.{html,vue}") { - yield { - name: "index.ts", - path: "fake-path", - }; - yield { - name: "index.js", - path: "fake-path", - }; - } - if (glob === "**/package.json") { - yield { - name: "package.json", - path: "fake-path", - }; - } - if (glob === "./package.json") { - yield { - name: "package.json", - path: "fake-path", - }; - } - if (glob === "**/.eslintrc.{js,cjs,yaml,yml,json}") { - yield { - name: ".eslintrc.js", - path: "fake-path", - }; - } - if (glob === "**/.eslintignore") { - yield { - name: ".eslintignore", - path: "fake-path", - }; - } - return; - }, - // deno-lint-ignore require-await - readJSON: async (path: string): Promise> => { - const deps = { stylelint: "1.0.0", eslint: "7.2.2" }; - const scripts = hasTestCommand ? { test: "yarn jest" } : {}; - if (path === "fake-path") { - return { - devDependencies: deps, - scripts: scripts, - }; - } - return {}; - }, - }, - }, - ); -}; - -Deno.test("Plugins > Check eslint and node for javascript project with a defined test command", async () => { - const result = await introspector.introspect( - fakeContext({ hasTestCommand: true }), - ); - - assertEquals(result, { - runtime: { name: "node", version: "16" }, - packageManager: { - name: "npm", - commands: { - install: "npm ci", - }, - }, - linters: { - eslint: { name: "eslint", hasIgnoreFile: false }, - }, - formatters: { prettier: { name: "prettier", hasIgnoreFile: false } }, - hasTestCommand: true, - type: null, - hasTypeScriptFiles: false, - }); -}); - -Deno.test("Plugins > Check eslint and node for javascript project with NO defined test command", async () => { - const result = await introspector.introspect( - fakeContext({ hasTestCommand: false }), - ); - - assertEquals(result, { - runtime: { name: "node", version: "16" }, - packageManager: { - name: "npm", - commands: { - install: "npm ci", - }, - }, - linters: { - eslint: { name: "eslint", hasIgnoreFile: false }, - }, - type: null, - formatters: { prettier: { name: "prettier", hasIgnoreFile: false } }, - hasTestCommand: false, - hasTypeScriptFiles: false, - }); -}); diff --git a/core/plugins/stack/javascript/type.ts b/core/plugins/stack/javascript/type.ts index cd7d734..c3e7040 100644 --- a/core/plugins/stack/javascript/type.ts +++ b/core/plugins/stack/javascript/type.ts @@ -1,5 +1,4 @@ import { IntrospectFn } from "../../../types.ts"; -import { hasDependencyAny } from "./dependencies.ts"; const nodeWebApps = new Set([ "express", @@ -33,8 +32,9 @@ const denoWebApps = new Set([ export const introspect: IntrospectFn = async (context) => { if ( - await hasDependencyAny(context, nodeWebApps) || - await hasDependencyAny(context, denoWebApps) + (await context.dependencies.some((dep) => + nodeWebApps.has(dep) || denoWebApps.has(dep) + )) ) { return "webApp"; } diff --git a/core/plugins/stack/mod.ts b/core/plugins/stack/mod.ts index 78e7c5c..bd37a86 100644 --- a/core/plugins/stack/mod.ts +++ b/core/plugins/stack/mod.ts @@ -46,15 +46,26 @@ export type { TerraformProject, }; +export type CSS = "css"; +export type HTML = "html"; +export type Docker = "docker"; +export type JavaScript = "javascript"; +export type Java = "java"; +export type Markdown = "markdown"; +export type Python = "python"; +export type Ruby = "ruby"; +export type Shell = "shell"; +export type Terraform = "terraform"; + export const introspectors = [ - { name: "css", ...CssIntrospector }, - { name: "docker", ...DockerIntrospector }, - { name: "html", ...HtmlIntrospector }, - { name: "javascript", ...JavaScriptIntrospector }, - { name: "java", ...JavaIntrospector }, - { name: "markdown", ...MarkdownIntrospector }, - { name: "python", ...PythonIntrospector }, - { name: "ruby", ...RubyIntrospector }, - { name: "shell", ...ShellIntrospector }, - { name: "terraform", ...TerraformIntrospector }, + { name: "css", ...CssIntrospector }, + { name: "docker", ...DockerIntrospector }, + { name: "html", ...HtmlIntrospector }, + { name: "javascript", ...JavaScriptIntrospector }, + { name: "java", ...JavaIntrospector }, + { name: "markdown", ...MarkdownIntrospector }, + { name: "python", ...PythonIntrospector }, + { name: "ruby", ...RubyIntrospector }, + { name: "shell", ...ShellIntrospector }, + { name: "terraform", ...TerraformIntrospector }, ]; diff --git a/core/plugins/stack/python/bandit.ts b/core/plugins/stack/python/bandit.ts index 4bd3f2b..e4e6a49 100644 --- a/core/plugins/stack/python/bandit.ts +++ b/core/plugins/stack/python/bandit.ts @@ -1,5 +1,4 @@ import { IntrospectFn } from "../../../types.ts"; -import { hasPythonDependency } from "./dependencies.ts"; export interface Bandit { name: "bandit"; @@ -7,7 +6,7 @@ export interface Bandit { } export const introspect: IntrospectFn = async (context) => { - const isDependency = await hasPythonDependency(context, "bandit"); + const isDependency = await context.dependencies.includes("bandit"); if (isDependency) { return { diff --git a/core/plugins/stack/python/black.ts b/core/plugins/stack/python/black.ts index a7938ca..e5e2294 100644 --- a/core/plugins/stack/python/black.ts +++ b/core/plugins/stack/python/black.ts @@ -1,5 +1,4 @@ import { IntrospectFn } from "../../../types.ts"; -import { hasPythonDependency } from "./dependencies.ts"; export interface Black { name: "black"; @@ -7,7 +6,7 @@ export interface Black { } export const introspect: IntrospectFn = async (context) => { - const isDependency = await hasPythonDependency(context, "black"); + const isDependency = context.dependencies.includes("black"); let hasBlackConfig = false; for await (const file of context.files.each("**/pyproject.toml")) { diff --git a/core/plugins/stack/python/django.ts b/core/plugins/stack/python/django.ts index c2792ae..c2e712d 100644 --- a/core/plugins/stack/python/django.ts +++ b/core/plugins/stack/python/django.ts @@ -1,7 +1,6 @@ import { IntrospectFn } from "../../../types.ts"; -import { hasPythonDependency } from "./dependencies.ts"; export const introspect: IntrospectFn = async (context) => { // Search for the Django dependency to define as a Django project - return await hasPythonDependency(context, "django"); + return await context.dependencies.includes("django"); }; diff --git a/core/plugins/stack/python/flake8.ts b/core/plugins/stack/python/flake8.ts index 631db2c..6fd51c2 100644 --- a/core/plugins/stack/python/flake8.ts +++ b/core/plugins/stack/python/flake8.ts @@ -1,5 +1,4 @@ import { IntrospectFn } from "../../../types.ts"; -import { hasPythonDependency } from "./dependencies.ts"; export interface Flake8 { name: "flake8"; @@ -7,7 +6,7 @@ export interface Flake8 { } export const introspect: IntrospectFn = async (context) => { - const isDependency = await hasPythonDependency(context, "flake8"); + const isDependency = await context.dependencies.includes("flake8"); // Search for any of the following files: // .flake8 const hasFlake8Config = await context.files.includes( diff --git a/core/plugins/stack/python/isort.ts b/core/plugins/stack/python/isort.ts index fc7c213..457b740 100644 --- a/core/plugins/stack/python/isort.ts +++ b/core/plugins/stack/python/isort.ts @@ -1,5 +1,4 @@ import { IntrospectFn } from "../../../types.ts"; -import { hasPythonDependency } from "./dependencies.ts"; export interface ISort { name: "isort"; @@ -7,7 +6,7 @@ export interface ISort { } export const introspect: IntrospectFn = async (context) => { - const isDependency = await hasPythonDependency(context, "isort"); + const isDependency = await context.dependencies.includes("isort"); let hasISortConfig = false; for await (const file of context.files.each("**/pyproject.toml")) { diff --git a/core/plugins/stack/python/mod.test.ts b/core/plugins/stack/python/mod.test.ts deleted file mode 100644 index cd588fd..0000000 --- a/core/plugins/stack/python/mod.test.ts +++ /dev/null @@ -1,381 +0,0 @@ -import { context } from "../../../tests/mod.ts"; -import { assertEquals, deepMerge } from "../../../deps.ts"; -import { FileEntry } from "../../../types.ts"; - -import { introspector } from "./mod.ts"; - -const fakeContext = ( - { - isDjango = false, - hasPytest = false, - isPoetry = false, - isPipenv = false, - isRequirements = false, - } = {}, -) => { - return deepMerge( - context, - { - files: { - // deno-lint-ignore require-await - includes: async (glob: string): Promise => { - if (glob === "**/*.py") { - return true; - } - if (glob === "**/manage.py" && isDjango) { - return true; - } - if (glob === "./poetry.lock" && isPoetry) { - return true; - } - if (glob === "./Pipfile.lock" && isPipenv) { - return true; - } - if (glob === "./requirements.txt" && isRequirements) { - return true; - } - return false; - }, - each: async function* (glob: string): AsyncIterableIterator { - if (glob === "**/.python-version") { - yield { - name: ".python-version", - path: "fake-path-root", - }; - } - if (glob === "./Pipfile" && isPipenv) { - yield { - name: "Pipfile", - path: "fake-path", - }; - } - if (glob === "./pyproject.toml" && isPoetry) { - yield { - name: "pyproject.toml", - path: "fake-path", - }; - } - if (glob === "./requirements.txt" && isRequirements) { - yield { - name: "requirements.txt", - path: "fake-path", - }; - } - return; - }, - // deno-lint-ignore require-await - readToml: async (path: string): Promise> => { - if (path === "fake-path") { - if (isPipenv) { - const dep = { - requires: { python_version: "3.6" }, - packages: {}, - "dev-packages": {}, - }; - if (isDjango) { - dep.packages = { django: "2.0.9" }; - } - if (hasPytest) { - dep["dev-packages"] = { pytest: "2" }; - } - return dep; - } - if (isPoetry) { - const dep = { - tool: { - poetry: { - dependencies: {}, - "dev-packages": {}, - }, - }, - }; - if (isDjango) { - dep.tool.poetry.dependencies = { django: "2.0.9" }; - } - if (hasPytest) { - dep.tool.poetry["dev-packages"] = { pytest: "2" }; - } - return dep; - } - } - - return {}; - }, - // deno-lint-ignore require-await - readText: async (path: string) => { - if (path === "fake-path-root") { - return "3.6"; - } - if (path === "fake-path") { - if (isRequirements) { - let dep = "dep == 0.1.0"; - if (isDjango) { - dep += "\ndjango == 2.0.9"; - } - if (hasPytest) { - dep += "\npytest == 2"; - } - return dep; - } - } - return ""; - }, - }, - }, - ); -}; - -Deno.test("Plugins > Check if python version and django project is identified PIPENV", async () => { - const result = await introspector.introspect( - fakeContext({ - isDjango: true, - isPipenv: true, - }), - ); - - assertEquals(result, { - version: "3.6", - formatters: { - black: { - isDependency: false, - name: "black", - }, - isort: { - isDependency: false, - name: "isort", - }, - }, - frameworks: { - django: {}, - }, - hasPytest: false, - linters: { - flake8: { - isDependency: false, - name: "flake8", - }, - bandit: { - isDependency: false, - name: "bandit", - }, - }, - packageManager: { - name: "pipenv", - commands: { - install: "python -m pip install pipenv; pipenv install --dev", - run: "pipenv run ", - }, - }, - type: "webApp", - }); -}); - -Deno.test("Plugins > Check if python version and a non-django-project PIPENV", async () => { - const result = await introspector.introspect( - fakeContext({ isDjango: false, isPipenv: true }), - ); - - assertEquals(result, { - version: "3.6", - formatters: { - black: { - isDependency: false, - name: "black", - }, - isort: { - isDependency: false, - name: "isort", - }, - }, - frameworks: {}, - hasPytest: false, - linters: { - flake8: { - isDependency: false, - name: "flake8", - }, - bandit: { - isDependency: false, - name: "bandit", - }, - }, - packageManager: { - name: "pipenv", - commands: { - install: "python -m pip install pipenv; pipenv install --dev", - run: "pipenv run ", - }, - }, - type: null, - }); -}); - -Deno.test("Plugins > Check if python version and django project is identified POETRY", async () => { - const result = await introspector.introspect( - fakeContext({ - isDjango: true, - isPoetry: true, - }), - ); - - assertEquals(result, { - version: "3.6", - formatters: { - black: { - isDependency: false, - name: "black", - }, - isort: { - isDependency: false, - name: "isort", - }, - }, - frameworks: { - django: {}, - }, - hasPytest: false, - linters: { - flake8: { - isDependency: false, - name: "flake8", - }, - bandit: { - isDependency: false, - name: "bandit", - }, - }, - packageManager: { - name: "poetry", - commands: { - install: "python -m pip install poetry; poetry install", - run: "poetry run ", - }, - }, - type: "webApp", - }); -}); - -Deno.test("Plugins > Check if python version and a non-django-project POETRY", async () => { - const result = await introspector.introspect( - fakeContext({ isDjango: false, isPoetry: true }), - ); - - assertEquals(result, { - version: "3.6", - formatters: { - black: { - isDependency: false, - name: "black", - }, - isort: { - isDependency: false, - name: "isort", - }, - }, - frameworks: {}, - hasPytest: false, - linters: { - flake8: { - isDependency: false, - name: "flake8", - }, - bandit: { - isDependency: false, - name: "bandit", - }, - }, - packageManager: { - name: "poetry", - commands: { - install: "python -m pip install poetry; poetry install", - run: "poetry run ", - }, - }, - type: null, - }); -}); - -Deno.test("Plugins > Check if python version and django project is identified REQ", async () => { - const result = await introspector.introspect( - fakeContext({ - isDjango: true, - isRequirements: true, - }), - ); - - assertEquals(result, { - version: "3.6", - formatters: { - black: { - isDependency: false, - name: "black", - }, - isort: { - isDependency: false, - name: "isort", - }, - }, - frameworks: { - django: {}, - }, - hasPytest: false, - linters: { - flake8: { - isDependency: false, - name: "flake8", - }, - bandit: { - isDependency: false, - name: "bandit", - }, - }, - packageManager: { - name: "pip", - commands: { - install: "pip install -r requirements.txt", - run: "", - }, - }, - type: "webApp", - }); -}); - -Deno.test("Plugins > Check if python version and a non-django-project REQ", async () => { - const result = await introspector.introspect( - fakeContext({ isDjango: false, isRequirements: true }), - ); - - assertEquals(result, { - version: "3.6", - formatters: { - black: { - isDependency: false, - name: "black", - }, - isort: { - isDependency: false, - name: "isort", - }, - }, - frameworks: {}, - hasPytest: false, - linters: { - flake8: { - isDependency: false, - name: "flake8", - }, - bandit: { - isDependency: false, - name: "bandit", - }, - }, - packageManager: { - name: "pip", - commands: { - install: "pip install -r requirements.txt", - run: "", - }, - }, - type: null, - }); -}); diff --git a/core/plugins/stack/python/pylint.ts b/core/plugins/stack/python/pylint.ts index 3303278..e05c716 100644 --- a/core/plugins/stack/python/pylint.ts +++ b/core/plugins/stack/python/pylint.ts @@ -1,5 +1,4 @@ import { IntrospectFn } from "../../../types.ts"; -import { hasPythonDependency } from "./dependencies.ts"; export interface PyLint { name: "pylint"; @@ -8,7 +7,7 @@ export interface PyLint { } export const introspect: IntrospectFn = async (context) => { - const isDependency = await hasPythonDependency(context, "pylint"); + const isDependency = await context.dependencies.includes("pylint"); let hasPyLintConfig = false; for await (const file of context.files.each("**/pyproject.toml")) { diff --git a/core/plugins/stack/python/pytest.ts b/core/plugins/stack/python/pytest.ts index 61134f9..c5b3207 100644 --- a/core/plugins/stack/python/pytest.ts +++ b/core/plugins/stack/python/pytest.ts @@ -1,7 +1,6 @@ import { IntrospectFn } from "../../../types.ts"; -import { hasPythonDependency } from "./dependencies.ts"; export const introspect: IntrospectFn = async (context) => { // Search for the Pytest dependency - return await hasPythonDependency(context, "pytest"); + return await context.dependencies.includes("pytest"); }; diff --git a/core/plugins/stack/python/type.ts b/core/plugins/stack/python/type.ts index 2f6f6ec..7461b22 100644 --- a/core/plugins/stack/python/type.ts +++ b/core/plugins/stack/python/type.ts @@ -1,5 +1,4 @@ import { IntrospectFn } from "../../../types.ts"; -import { hasPythonDependencyAny } from "./dependencies.ts"; const webApps = new Set([ "uvicorn", @@ -29,7 +28,7 @@ const webApps = new Set([ ]); export const introspect: IntrospectFn = async (context) => { - if (await hasPythonDependencyAny(context, webApps)) { + if (await context.dependencies.some((dep) => webApps.has(dep))) { return "webApp"; } return null; diff --git a/core/plugins/stack/ruby/rubocop.ts b/core/plugins/stack/ruby/rubocop.ts index 711fd9e..76d7b36 100644 --- a/core/plugins/stack/ruby/rubocop.ts +++ b/core/plugins/stack/ruby/rubocop.ts @@ -1,5 +1,4 @@ import { IntrospectFn } from "../../../types.ts"; -import { hasRubyDependency } from "./dependencies.ts"; export interface Rubocop { name: "rubocop"; @@ -7,7 +6,7 @@ export interface Rubocop { } export const introspect: IntrospectFn = async (context) => { - const isDependency = await hasRubyDependency(context, "rubocop"); + const isDependency = await context.dependencies.includes("rubocop"); // Search for any of the following files: // .rubocop.yml // Reference: https://docs.rubocop.org/rubocop/configuration.html#config-file-locations diff --git a/core/plugins/stack/ruby/types.ts b/core/plugins/stack/ruby/types.ts index d94ebcc..bafdd12 100644 --- a/core/plugins/stack/ruby/types.ts +++ b/core/plugins/stack/ruby/types.ts @@ -1,5 +1,4 @@ import { IntrospectFn } from "../../../types.ts"; -import { hasRubyDependencyAny } from "./dependencies.ts"; const webApps = new Set([ "rails", @@ -11,7 +10,7 @@ const webApps = new Set([ ]); export const introspect: IntrospectFn = async (context) => { - if (await hasRubyDependencyAny(context, webApps)) { + if ((await context.dependencies.some((dep) => webApps.has(dep)))) { return "webApp"; } return null; diff --git a/core/tests/mod.ts b/core/tests/mod.ts index 2f4924d..d3be6f9 100644 --- a/core/tests/mod.ts +++ b/core/tests/mod.ts @@ -43,6 +43,7 @@ export const context: Context = { errors: { add(_) {}, }, + dependencies: [], semver: semver, suggestDefault: true, strict: true, diff --git a/core/types.ts b/core/types.ts index 5cae56e..9e49f5c 100644 --- a/core/types.ts +++ b/core/types.ts @@ -41,6 +41,7 @@ interface SemVerHelpers { export type Context = { getLogger(name?: string): Logger; + dependencies: string[]; files: { each(glob: string): AsyncIterableIterator; includes(glob: string): Promise;