Skip to content

Commit

Permalink
Analyze all NPM packages with stimulus in the package name
Browse files Browse the repository at this point in the history
  • Loading branch information
marcoroth committed Feb 11, 2024
1 parent 57f81ff commit eeeb869
Show file tree
Hide file tree
Showing 10 changed files with 271 additions and 11 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
/dist
node_modules/
coverage/
scratch/

yarn-error.log

Expand Down
50 changes: 50 additions & 0 deletions src/packages/analyze.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import path from "path"
import { glob } from "glob"

import type { NodeModule } from "../types"
import { Project } from "../project"
import { readFile } from "../util"
import { findNodeModulesPath } from "./util"

export async function analyzeAll(project: Project) {
const nodeModulesPath = await findNodeModulesPath(project.projectPath)

if (!nodeModulesPath) return

const packages = [
...await glob(`${nodeModulesPath}/*stimulus*/package.json`), // for libraries like stimulus-in-library
...await glob(`${nodeModulesPath}/*stimulus*/*/package.json`), // for libraries like @stimulus-in-namespace/some-library
...await glob(`${nodeModulesPath}/*/*stimulus*/package.json`), // for libraries like @some-namespace/stimulus-in-library
]

await Promise.allSettled(
packages.map(async packagePath => {
const folder = path.dirname(packagePath)
const packageJSON = await readFile(packagePath)
const parsed = JSON.parse(packageJSON)
const packageName = parsed.name

if (packageName === "@hotwired/stimulus") return
if (packageName === "@hotwired/stimulus-webpack-helpers") return
if (packageName === "stimulus-vite-helpers") return

const source = parsed.source || parsed.module || parsed.main

if (source) {
const directory = path.dirname(source)
const basePath = path.join(folder, directory)
const files = await glob(`${basePath}/**/*.{js,mjs}`)

const detectedModule: NodeModule = {
name: packageName,
path: packagePath,
controllerRoots: [basePath]
}

project.detectedNodeModules.push(detectedModule)

await project.readControllerFiles(files)
}
})
)
}
8 changes: 5 additions & 3 deletions src/packages/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Project } from '../project'
import { Project } from "../project"

import * as tailwindcssStimulusComponents from "./tailwindcss-stimulus-components"
// import * as tailwindcssStimulusComponents from "./tailwindcss-stimulus-components"
import { analyzeAll } from "./analyze"

export async function detectPackages(project: Project) {
await tailwindcssStimulusComponents.analyze(project)
// await tailwindcssStimulusComponents.analyze(project)
await analyzeAll(project)
}
2 changes: 1 addition & 1 deletion src/packages/tailwindcss-stimulus-components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import path from "path"
import { glob } from "glob"

import type { NodeModule } from "../types"
import { Project } from '../project'
import { Project } from "../project"
import { hasDepedency, findPackagePath } from "./util"

export async function analyze(project: Project) {
Expand Down
4 changes: 2 additions & 2 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export class Parser {

ClassDeclaration(node: any): void {
const className = node.id?.name
const superClass = node.superClass.name
const superClass = node.superClass?.name
const importStatement = importStatements.find(i => i.importedName === superClass)

// TODO: this needs to be recursive
Expand All @@ -85,7 +85,7 @@ export class Parser {
}
} else {
controller.parent = {
constant: node.superClass.name,
constant: superClass,
type: "unknown",
}
}
Expand Down
11 changes: 6 additions & 5 deletions src/project.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { ControllerDefinition } from "./controller_definition"
import { Parser } from "./parser"
import { resolvePathWhenFileExists, nestedFolderSort } from "./util"
import { readFile, resolvePathWhenFileExists, nestedFolderSort } from "./util"
import { detectPackages } from "./packages"
import type { NodeModule } from "./types"

import path from "path"
import { promises as fs } from "fs"
import { glob } from "glob"

interface ControllerFile {
Expand Down Expand Up @@ -134,7 +133,7 @@ export class Project {
async readControllerFiles(controllerFiles: string[]) {
await Promise.allSettled(
controllerFiles.map(async (filename: string) => {
const content = await fs.readFile(filename, "utf8")
const content = await readFile(filename)

this.controllerFiles.push({ filename, content })
})
Expand All @@ -143,11 +142,13 @@ export class Project {

private async getControllerFiles(): Promise<string[]> {
return await glob(`${this.projectPath}/**/*controller${this.fileEndingsGlob}`, {
ignore: `${this.projectPath}/node_modules/**/*`,
ignore: `${this.projectPath}/**/node_modules/**/*`,
})
}

get fileEndingsGlob(): string {
return `.{${Project.javascriptEndings.join(",")},${Project.typescriptEndings.join(",")}}`
const extensions = Project.javascriptEndings.concat(Project.typescriptEndings).join(",")

return `.{${extensions}}`
}
}
4 changes: 4 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export async function resolvePathWhenFileExists(path: string): Promise<string|nu
return exists ? path : null
}

export async function readFile(path: string): Promise<string> {
return await fs.readFile(path, "utf8")
}

export async function fileExists(path: string): Promise<boolean> {
return folderExists(path)
}
Expand Down
18 changes: 18 additions & 0 deletions test/fixtures/packages/app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "app",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"@hotwired/stimulus": "^3.2.2",
"@stimulus-library/controllers": "^1.0.6",
"stimulus-checkbox": "^2.0.0",
"stimulus-clipboard": "^4.0.1",
"stimulus-datepicker": "^1.0.6",
"stimulus-dropdown": "^2.1.0",
"stimulus-hotkeys": "^2.3.0",
"stimulus-inline-input-validations": "^1.2.0",
"stimulus-use": "^0.52.2",
"tailwindcss-stimulus-components": "^4.0.4"
}
}
117 changes: 117 additions & 0 deletions test/fixtures/packages/app/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


"@babel/runtime@^7.21.0":
version "7.23.9"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.9.tgz#47791a15e4603bb5f905bc0753801cf21d6345f7"
integrity sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==
dependencies:
regenerator-runtime "^0.14.0"

"@hotwired/stimulus@^3.0.0", "@hotwired/stimulus@^3.0.1", "@hotwired/stimulus@^3.2.2":
version "3.2.2"
resolved "https://registry.yarnpkg.com/@hotwired/stimulus/-/stimulus-3.2.2.tgz#071aab59c600fed95b97939e605ff261a4251608"
integrity sha512-eGeIqNOQpXoPAIP7tC1+1Yc1yl1xnwYqg+3mzqxyrbE5pg5YFBZcA6YoTiByJB6DKAEsiWtl6tjTJS4IYtbB7A==

"@stimulus-library/controllers@^1.0.6":
version "1.0.6"
resolved "https://registry.yarnpkg.com/@stimulus-library/controllers/-/controllers-1.0.6.tgz#8122303e6dfa1139abda15e5aa41b81ce303bedf"
integrity sha512-cdA4NmiHI2oLImHcphtJnVmhxQ2Krz2Nh85UlXez0US9OS50SiVkxe+nAajt0eJ5o6HHfrr5SUxiUilQqlK90g==
dependencies:
"@stimulus-library/mixins" "^1.0.5"
"@stimulus-library/utilities" "^1.0.5"
date-fns "^2.29.3"

"@stimulus-library/mixins@^1.0.5":
version "1.0.5"
resolved "https://registry.yarnpkg.com/@stimulus-library/mixins/-/mixins-1.0.5.tgz#4b8834e7279978caf4ea277735d40c350b1e56f2"
integrity sha512-saPVDcYVMTFAebaRJ4IAoNdEnfJpMIQxMgQf/CNugEDbxQb6+r7ZEIW3AmXedkKda3roN8TKrL7nUacXWxaz5Q==
dependencies:
"@hotwired/stimulus" "^3.0.0"
"@stimulus-library/utilities" "^1.0.5"

"@stimulus-library/utilities@^1.0.2", "@stimulus-library/utilities@^1.0.5":
version "1.0.5"
resolved "https://registry.yarnpkg.com/@stimulus-library/utilities/-/utilities-1.0.5.tgz#8f790e1f13e64553c0bce617f6f3b56b7be77422"
integrity sha512-cTJk4OQwMy+VaXhSRszfZuIgt2GY49fn3IzktS15IV2qdVsaxF66mIdbuYRJgdIpMsQzQ/1gW5iS2P10iDpFpg==
dependencies:
"@hotwired/stimulus" "^3.0.0"
"@stimulus-library/utilities" "^1.0.2"
mitt "^3.0.0"

date-fns@^2.29.3:
version "2.30.0"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0"
integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==
dependencies:
"@babel/runtime" "^7.21.0"

hotkeys-js@>=3, hotkeys-js@^3.8.7:
version "3.13.7"
resolved "https://registry.yarnpkg.com/hotkeys-js/-/hotkeys-js-3.13.7.tgz#0188d8e2fca16a3f1d66541b48de0bb9df613726"
integrity sha512-ygFIdTqqwG4fFP7kkiYlvayZppeIQX2aPpirsngkv1xM1lP0piDY5QEh68nQnIKvz64hfocxhBaD/uK3sSK1yQ==

mitt@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.1.tgz#ea36cf0cc30403601ae074c8f77b7092cdab36d1"
integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==

regenerator-runtime@^0.14.0:
version "0.14.1"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f"
integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==

stimulus-checkbox@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/stimulus-checkbox/-/stimulus-checkbox-2.0.0.tgz#ee4908ca6263556f0015eb36495ddf70611bca56"
integrity sha512-tuflIcPariCD6Ju/qoRXLbZR+f5FiZKVHYCA3Qj6i3PVqhYG0pgEYCPBKT3bmbSIZsAfK9Xc1pi+zryqlfjl0g==
dependencies:
"@hotwired/stimulus" "^3.2.2"

stimulus-clipboard@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/stimulus-clipboard/-/stimulus-clipboard-4.0.1.tgz#acc9212b479fedc633ecdec8f191a28c1d826a6b"
integrity sha512-dem+ihC3Q8+gbyGINdd+dK+9d5vUTnOwoH+n3KcDJvbxrFcq9lV8mWjyhEaDquGxYy3MmqSdz9FHQbG88TBqGg==

stimulus-datepicker@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/stimulus-datepicker/-/stimulus-datepicker-1.0.6.tgz#64eb8895a8aae902fd60b672f3c957a7f9b42158"
integrity sha512-MP2WcmibFTqb76iRLEP/TC32FKmU9Ca6xeptQiov6v8PivgZ1YMGmr7fb34zrdTHiwVHgzjxTm/6oP5kis1iuQ==

stimulus-dropdown@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/stimulus-dropdown/-/stimulus-dropdown-2.1.0.tgz#22f15cd1dc247e08f04c3f95d7ab9d8102602a07"
integrity sha512-p4Bs56/ilB2E0lfFaNajKIHZK1PMUUDnhDl74f97bn087fxIfRB7WQekVtTTWJdRlf3EIgSsDX7K1TsaLiIcLg==
dependencies:
stimulus-use "^0.51.1"

stimulus-hotkeys@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/stimulus-hotkeys/-/stimulus-hotkeys-2.3.0.tgz#95e5c25cc47ca4eaa82a65831d57dae8cc97640a"
integrity sha512-oDQu7yrrEAsu3ohFAI1E7hNMIn/4IGLtGlyRoAds0Q3JO509Ua0MhoQkBYl0ZdVOMOKTZchjjIDhT2pNUaZHWQ==
dependencies:
"@hotwired/stimulus" "^3.0.1"
hotkeys-js "^3.8.7"

stimulus-inline-input-validations@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/stimulus-inline-input-validations/-/stimulus-inline-input-validations-1.2.0.tgz#5d8893e3f330044214e761cca2a73dcb715bb68a"
integrity sha512-3TVEmM9YBsVJdW2boux7C50+mnfikH0cMJ38ZinKmbVfXnGR1Sdyaweqe3FPOU8o2ktmWSRzYzk1n63y3hls8w==

stimulus-use@^0.51.1:
version "0.51.3"
resolved "https://registry.yarnpkg.com/stimulus-use/-/stimulus-use-0.51.3.tgz#d7ac671aff8d0db253296dec89d38aa6f4b27e2a"
integrity sha512-V4YETxMFL4/bpmcqlwFtaOaJg9sLF+XlWsvXrsoWVA5jffsqe7uAvV6gGPPQta7Hgx01vovA0yNsWUe2eU9Jmw==
dependencies:
hotkeys-js ">=3"

stimulus-use@^0.52.2:
version "0.52.2"
resolved "https://registry.yarnpkg.com/stimulus-use/-/stimulus-use-0.52.2.tgz#fc992fababe03f8d8bc2d9470c8cdb40bd075917"
integrity sha512-413+tIw9n6Jnb0OFiQE1i3aP01i0hhGgAnPp1P6cNuBbhhqG2IOp8t1O/4s5Tw2lTvSYrFeLNdaY8sYlDaULeg==

tailwindcss-stimulus-components@^4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/tailwindcss-stimulus-components/-/tailwindcss-stimulus-components-4.0.4.tgz#1df5f2a488aa89365561bb33357095cd59ed831a"
integrity sha512-xNlMs1WufKiTMQtVklwHfrR/iuPVaFA0Mk5uefRnHztmr7w4g6BzKAWHyfte60pjhcQbmlbshHMOZiq/dkXnhw==
67 changes: 67 additions & 0 deletions test/packages/app.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { describe, test, expect } from "vitest"
import { Project } from "../../src"

const project = new Project(`${process.cwd()}/test/fixtures/packages/app`)

describe("packages", () => {
describe("app", () => {
test("detects controllers", async () => {
expect(project.controllerDefinitions.length).toEqual(0)

await project.analyze()

expect(project.detectedNodeModules.map(module => module.name).sort()).toEqual([
"@stimulus-library/controllers",
"@stimulus-library/mixins",
"@stimulus-library/utilities",
"stimulus-checkbox",
"stimulus-clipboard",
"stimulus-datepicker",
"stimulus-dropdown",
"stimulus-hotkeys",
"stimulus-inline-input-validations",
"stimulus-use",
"tailwindcss-stimulus-components",
])

expect(project.controllerRoots).toEqual([
"node_modules/@stimulus-library/controllers",
"node_modules/stimulus-checkbox/src",
"node_modules/stimulus-clipboard/dist",
"node_modules/stimulus-datepicker/src",
"node_modules/stimulus-dropdown/dist",
"node_modules/stimulus-hotkeys/src",
"node_modules/stimulus-inline-input-validations/src",
"node_modules/stimulus-use/dist",
"node_modules/tailwindcss-stimulus-components/src",
"node_modules/@stimulus-library/mixins/dist",
"node_modules/@stimulus-library/utilities/dist",
])

// expect(project.controllerDefinitions.length).toEqual(11)
// expect(project.controllerDefinitions.map(controller => controller.identifier).sort()).toEqual([
// "alert",
// "autosave",
// "color-preview",
// "datepicker",
// "dropdown",
// "index",
// "input-validator",
// "iso-date",
// "modal",
// "popover",
// "slideover",
// "tabs",
// "toggle",
// "transition",
// ])

const controller = project.controllerDefinitions.find(controller => controller.identifier === "modal")

expect(controller.targets).toEqual(["container", "background"])
expect(Object.keys(controller.values)).toEqual(["open", "restoreScroll"])
expect(controller.values.open.type).toEqual("Boolean")
expect(controller.values.restoreScroll.type).toEqual("Boolean")
})
})
})

0 comments on commit eeeb869

Please sign in to comment.