From 4d5624e48495593ef4bc60d798d2057ff6d38322 Mon Sep 17 00:00:00 2001 From: Svyatoslav Kryukov Date: Sun, 12 May 2024 19:44:16 +0300 Subject: [PATCH] Setup package lint --- .github/workflows/main.yml | 2 +- .github/workflows/package.yml | 25 +++++ packages/turbo-mount/.eslintrc.json | 13 +++ packages/turbo-mount/.prettierrc | 6 ++ packages/turbo-mount/package.json | 11 ++- packages/turbo-mount/src/helpers.ts | 4 +- packages/turbo-mount/src/index.ts | 4 +- .../turbo-mount/src/plugins/react/index.ts | 10 +- .../react/turbo-mount-react-controller.ts | 20 ++-- .../turbo-mount/src/plugins/svelte/index.ts | 10 +- .../svelte/turbo-mount-svelte-controller.ts | 16 ++-- packages/turbo-mount/src/plugins/vue/index.ts | 10 +- .../plugins/vue/turbo-mount-vue-controller.ts | 16 ++-- .../turbo-mount/src/turbo-mount-controller.ts | 92 ++++++++++--------- packages/turbo-mount/src/turbo-mount.ts | 83 +++++++++-------- packages/turbo-mount/src/vite.ts | 89 ++++++++++-------- 16 files changed, 244 insertions(+), 167 deletions(-) create mode 100644 .github/workflows/package.yml create mode 100644 packages/turbo-mount/.eslintrc.json create mode 100644 packages/turbo-mount/.prettierrc diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6d57e77..72693b8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: ruby: - - '3.2.3' + - "3.3" steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml new file mode 100644 index 0000000..706b8e1 --- /dev/null +++ b/.github/workflows/package.yml @@ -0,0 +1,25 @@ +name: Package + +on: + push: + branches: + - main + + pull_request: + +jobs: + lint: + defaults: + run: + working-directory: "packages/turbo-mount" + + runs-on: ubuntu-latest + name: Run CI Lint + steps: + - uses: actions/checkout@v4 + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + - run: npm install --ignore-scripts + - run: npm run ci diff --git a/packages/turbo-mount/.eslintrc.json b/packages/turbo-mount/.eslintrc.json new file mode 100644 index 0000000..4f023f3 --- /dev/null +++ b/packages/turbo-mount/.eslintrc.json @@ -0,0 +1,13 @@ +{ + "parser": "@typescript-eslint/parser", + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended" + ], + "parserOptions": { + "ecmaVersion": 2017, + "sourceType": "module", + "project": "./tsconfig.json" + } +} diff --git a/packages/turbo-mount/.prettierrc b/packages/turbo-mount/.prettierrc new file mode 100644 index 0000000..a4e1e1c --- /dev/null +++ b/packages/turbo-mount/.prettierrc @@ -0,0 +1,6 @@ +{ + "semi": true, + "trailingComma": "all", + "printWidth": 80, + "tabWidth": 2 +} diff --git a/packages/turbo-mount/package.json b/packages/turbo-mount/package.json index d9f4f22..d17ce10 100644 --- a/packages/turbo-mount/package.json +++ b/packages/turbo-mount/package.json @@ -13,11 +13,14 @@ "url": "https://github.com/skryukov/turbo-mount/issues" }, "scripts": { + "lint": "eslint 'src/**/*.ts'", + "format": "prettier --write 'src/**/*.ts'", "clean": "rm -rf dist", "types": "tsc --noEmit false --declaration true --emitDeclarationOnly true --outDir dist/types", "build": "npm run types && rollup -c", "prerelease": "npm run clean && npm run build && git --no-pager diff && echo && npm pack --dry-run", - "copy": "cp dist/{*.js,*.map} ../../app/assets/javascripts/ && mkdir -p ../../app/assets/javascripts/turbo-mount && cp dist/plugins/* ../../app/assets/javascripts/turbo-mount/" + "copy": "cp dist/{*.js,*.map} ../../app/assets/javascripts/ && mkdir -p ../../app/assets/javascripts/turbo-mount && cp dist/plugins/* ../../app/assets/javascripts/turbo-mount/", + "ci": "npm run lint && npm run build" }, "module": "dist/turbo-mount.js", "types": "dist/types/index.d.ts", @@ -37,6 +40,12 @@ "@rollup/plugin-typescript": "^11.1.6", "@types/react": "^18.3.1", "@types/react-dom": "^18.3.0", + "@typescript-eslint/eslint-plugin": "^7.8.0", + "@typescript-eslint/parser": "^7.8.0", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "prettier": "^3.2.5", "react": ">= 17.0", "react-dom": ">= 17.0", "rollup": "^2.79.1", diff --git a/packages/turbo-mount/src/helpers.ts b/packages/turbo-mount/src/helpers.ts index d591457..0a728d7 100644 --- a/packages/turbo-mount/src/helpers.ts +++ b/packages/turbo-mount/src/helpers.ts @@ -1,3 +1,3 @@ export const camelToKebabCase = (str: string) => { - return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); -} + return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(); +}; diff --git a/packages/turbo-mount/src/index.ts b/packages/turbo-mount/src/index.ts index 109183b..6736aea 100644 --- a/packages/turbo-mount/src/index.ts +++ b/packages/turbo-mount/src/index.ts @@ -1,2 +1,2 @@ -export {TurboMountController} from "./turbo-mount-controller" -export * from "./turbo-mount" +export { TurboMountController } from "./turbo-mount-controller"; +export * from "./turbo-mount"; diff --git a/packages/turbo-mount/src/plugins/react/index.ts b/packages/turbo-mount/src/plugins/react/index.ts index 376dd85..51d48a6 100644 --- a/packages/turbo-mount/src/plugins/react/index.ts +++ b/packages/turbo-mount/src/plugins/react/index.ts @@ -1,10 +1,10 @@ -import {Plugin} from "turbo-mount"; +import { Plugin } from "turbo-mount"; -import {TurboMountReactController} from "./turbo-mount-react-controller"; +import { TurboMountReactController } from "./turbo-mount-react-controller"; const plugin: Plugin = { - framework: "react", - controller: TurboMountReactController -} + framework: "react", + controller: TurboMountReactController, +}; export default plugin; diff --git a/packages/turbo-mount/src/plugins/react/turbo-mount-react-controller.ts b/packages/turbo-mount/src/plugins/react/turbo-mount-react-controller.ts index 0ff9fd7..a83a63a 100644 --- a/packages/turbo-mount/src/plugins/react/turbo-mount-react-controller.ts +++ b/packages/turbo-mount/src/plugins/react/turbo-mount-react-controller.ts @@ -1,14 +1,16 @@ -import {ComponentType, createElement} from "react"; -import {createRoot} from "react-dom/client"; -import {TurboMountController} from "turbo-mount"; +import { ComponentType, createElement } from "react"; +import { createRoot } from "react-dom/client"; +import { TurboMountController } from "turbo-mount"; export class TurboMountReactController extends TurboMountController { - framework = "react" + framework = "react"; - mountComponent(el: Element, Component: ComponentType, props: object) { - const root = createRoot(el); - root.render(createElement(Component, props)) + mountComponent(el: Element, Component: ComponentType, props: object) { + const root = createRoot(el); + root.render(createElement(Component, props)); - return () => { root.unmount() } - } + return () => { + root.unmount(); + }; + } } diff --git a/packages/turbo-mount/src/plugins/svelte/index.ts b/packages/turbo-mount/src/plugins/svelte/index.ts index 34956e0..304eb50 100644 --- a/packages/turbo-mount/src/plugins/svelte/index.ts +++ b/packages/turbo-mount/src/plugins/svelte/index.ts @@ -1,10 +1,10 @@ -import {Plugin} from "turbo-mount"; +import { Plugin } from "turbo-mount"; -import {TurboMountSvelteController} from "./turbo-mount-svelte-controller"; +import { TurboMountSvelteController } from "./turbo-mount-svelte-controller"; const plugin: Plugin = { - framework: "svelte", - controller: TurboMountSvelteController -} + framework: "svelte", + controller: TurboMountSvelteController, +}; export default plugin; diff --git a/packages/turbo-mount/src/plugins/svelte/turbo-mount-svelte-controller.ts b/packages/turbo-mount/src/plugins/svelte/turbo-mount-svelte-controller.ts index ab5e6ec..af14e1d 100644 --- a/packages/turbo-mount/src/plugins/svelte/turbo-mount-svelte-controller.ts +++ b/packages/turbo-mount/src/plugins/svelte/turbo-mount-svelte-controller.ts @@ -1,12 +1,14 @@ -import {ComponentType} from "svelte"; -import {TurboMountController} from "turbo-mount"; +import { ComponentType } from "svelte"; +import { TurboMountController } from "turbo-mount"; export class TurboMountSvelteController extends TurboMountController { - framework = "svelte" + framework = "svelte"; - mountComponent(el: Element, Component: ComponentType, props: object) { - const component = new Component({ target: el, props }) + mountComponent(el: Element, Component: ComponentType, props: object) { + const component = new Component({ target: el, props }); - return () => { component.$destroy() } - } + return () => { + component.$destroy(); + }; + } } diff --git a/packages/turbo-mount/src/plugins/vue/index.ts b/packages/turbo-mount/src/plugins/vue/index.ts index 79ce85f..f87331f 100644 --- a/packages/turbo-mount/src/plugins/vue/index.ts +++ b/packages/turbo-mount/src/plugins/vue/index.ts @@ -1,10 +1,10 @@ -import {Plugin} from "turbo-mount"; +import { Plugin } from "turbo-mount"; -import {TurboMountVueController} from "./turbo-mount-vue-controller"; +import { TurboMountVueController } from "./turbo-mount-vue-controller"; const plugin: Plugin = { - framework: "vue", - controller: TurboMountVueController -} + framework: "vue", + controller: TurboMountVueController, +}; export default plugin; diff --git a/packages/turbo-mount/src/plugins/vue/turbo-mount-vue-controller.ts b/packages/turbo-mount/src/plugins/vue/turbo-mount-vue-controller.ts index 28fa6c7..d380717 100644 --- a/packages/turbo-mount/src/plugins/vue/turbo-mount-vue-controller.ts +++ b/packages/turbo-mount/src/plugins/vue/turbo-mount-vue-controller.ts @@ -1,13 +1,15 @@ import { createApp, App } from "vue"; -import {TurboMountController} from "turbo-mount"; +import { TurboMountController } from "turbo-mount"; export class TurboMountVueController extends TurboMountController { - framework = "vue" + framework = "vue"; - mountComponent(el: Element, Component: App, props: object) { - const app = createApp(Component, props as Record); - app.mount(el) + mountComponent(el: Element, Component: App, props: object) { + const app = createApp(Component, props as Record); + app.mount(el); - return () => { app.unmount() } - } + return () => { + app.unmount(); + }; + } } diff --git a/packages/turbo-mount/src/turbo-mount-controller.ts b/packages/turbo-mount/src/turbo-mount-controller.ts index 350a6ef..3f58d21 100644 --- a/packages/turbo-mount/src/turbo-mount-controller.ts +++ b/packages/turbo-mount/src/turbo-mount-controller.ts @@ -1,58 +1,64 @@ -import {Controller} from "@hotwired/stimulus" -import {ApplicationWithTurboMount} from "./turbo-mount"; +import { Controller } from "@hotwired/stimulus"; +import { ApplicationWithTurboMount } from "./turbo-mount"; export abstract class TurboMountController extends Controller { - static values = { - props: Object, - component: String - } - static targets = [ "mount" ] + static values = { + props: Object, + component: String, + }; + static targets = ["mount"]; - declare readonly propsValue: object; - declare readonly componentValue: string; - declare readonly hasMountTarget: boolean; - declare readonly mountTarget: Element; + declare readonly propsValue: object; + declare readonly componentValue: string; + declare readonly hasMountTarget: boolean; + declare readonly mountTarget: Element; - abstract framework: string; + abstract framework: string; - abstract mountComponent(el: Element, Component: T, props: object): () => void; + abstract mountComponent(el: Element, Component: T, props: object): () => void; - _umountComponentCallback?: () => void; + _umountComponentCallback?: () => void; - connect() { - this._umountComponentCallback ||= this.mountComponent(this.mountElement, this.resolvedComponent, this.componentProps); - } + connect() { + this._umountComponentCallback ||= this.mountComponent( + this.mountElement, + this.resolvedComponent, + this.componentProps, + ); + } - disconnect() { - this.umountComponent(); - } + disconnect() { + this.umountComponent(); + } - propsValueChanged() { - this.umountComponent(); - this._umountComponentCallback ||= this.mountComponent(this.mountElement, this.resolvedComponent, this.componentProps); - } + propsValueChanged() { + this.umountComponent(); + this._umountComponentCallback ||= this.mountComponent( + this.mountElement, + this.resolvedComponent, + this.componentProps, + ); + } - get componentProps() { - return this.propsValue; - } + get componentProps() { + return this.propsValue; + } - get mountElement() { - return this.hasMountTarget ? this.mountTarget : this.element; - } + get mountElement() { + return this.hasMountTarget ? this.mountTarget : this.element; + } - get resolvedComponent() { - return this.resolveComponent(this.componentValue); - } + get resolvedComponent() { + return this.resolveComponent(this.componentValue); + } - umountComponent() { - this._umountComponentCallback && this._umountComponentCallback(); - this._umountComponentCallback = undefined; - } + umountComponent() { + this._umountComponentCallback && this._umountComponentCallback(); + this._umountComponentCallback = undefined; + } - resolveComponent(component: string): T { - const app = this.application as ApplicationWithTurboMount - return app.turboMount[this.framework].resolve(component); - } + resolveComponent(component: string): T { + const app = this.application as ApplicationWithTurboMount; + return app.turboMount[this.framework].resolve(component); + } } - - diff --git a/packages/turbo-mount/src/turbo-mount.ts b/packages/turbo-mount/src/turbo-mount.ts index a827cfd..0b3a850 100644 --- a/packages/turbo-mount/src/turbo-mount.ts +++ b/packages/turbo-mount/src/turbo-mount.ts @@ -1,54 +1,57 @@ -import {Application, ControllerConstructor} from '@hotwired/stimulus'; +import { Application, ControllerConstructor } from "@hotwired/stimulus"; -import {camelToKebabCase} from "./helpers"; +import { camelToKebabCase } from "./helpers"; export interface ApplicationWithTurboMount extends Application { - turboMount: { [framework: string]: TurboMount }; + turboMount: { [framework: string]: TurboMount }; } export type Plugin = { - framework: string; - controller: ControllerConstructor; -} + framework: string; + controller: ControllerConstructor; +}; export class TurboMount { - components: Map; - application: ApplicationWithTurboMount; - framework: string; - baseController?: ControllerConstructor; - - constructor(props: { application: Application, plugin: Plugin }) { - this.components = new Map(); - this.application = props.application as ApplicationWithTurboMount; - this.framework = props.plugin.framework; - this.baseController = props.plugin.controller; - - this.application.turboMount ||= {}; - this.application.turboMount[this.framework] = this; - - if (this.baseController) { - this.application.register(`turbo-mount-${this.framework}`, this.baseController); - } + components: Map; + application: ApplicationWithTurboMount; + framework: string; + baseController?: ControllerConstructor; + + constructor(props: { application: Application; plugin: Plugin }) { + this.components = new Map(); + this.application = props.application as ApplicationWithTurboMount; + this.framework = props.plugin.framework; + this.baseController = props.plugin.controller; + + this.application.turboMount ||= {}; + this.application.turboMount[this.framework] = this; + + if (this.baseController) { + this.application.register( + `turbo-mount-${this.framework}`, + this.baseController, + ); + } + } + + register(name: string, component: T, controller?: ControllerConstructor) { + controller ||= this.baseController; + if (this.components.has(name)) { + throw new Error(`Component '${name}' is already registered.`); } + this.components.set(name, component); - register(name: string, component: T, controller?: ControllerConstructor) { - controller ||= this.baseController; - if (this.components.has(name)) { - throw new Error(`Component '${name}' is already registered.`); - } - this.components.set(name, component); - - if (controller) { - const controllerName = `turbo-mount-${this.framework}-${camelToKebabCase(name)}`; - this.application.register(controllerName, controller); - } + if (controller) { + const controllerName = `turbo-mount-${this.framework}-${camelToKebabCase(name)}`; + this.application.register(controllerName, controller); } + } - resolve(name: string) { - const component = this.components.get(name); - if (!component) { - throw new Error(`Unknown component: ${name}`); - } - return component; + resolve(name: string) { + const component = this.components.get(name); + if (!component) { + throw new Error(`Unknown component: ${name}`); } + return component; + } } diff --git a/packages/turbo-mount/src/vite.ts b/packages/turbo-mount/src/vite.ts index f3cd099..30be994 100644 --- a/packages/turbo-mount/src/vite.ts +++ b/packages/turbo-mount/src/vite.ts @@ -1,45 +1,54 @@ -import {definitionsFromGlob} from "stimulus-vite-helpers"; -import {Definition} from "@hotwired/stimulus" +import { definitionsFromGlob } from "stimulus-vite-helpers"; +import { Definition } from "@hotwired/stimulus"; -import {TurboMount} from "turbo-mount"; +import { TurboMount } from "turbo-mount"; -import {camelToKebabCase} from "./helpers"; +import { camelToKebabCase } from "./helpers"; + +type ComponentModule = { default: never } | never; type RegisterComponentsProps = { - turboMount: TurboMount; - components: Record; - controllers?: Record; -} - -const identifierNames = (name: string, turboMount: TurboMount) => { - const controllerName = camelToKebabCase(name); - const framework = turboMount.framework; - - return [ - `turbo-mount--${framework}--${controllerName}`, - `turbo-mount--${framework}-${controllerName}`, - `turbo-mount-${framework}-${controllerName}`, - `turbo-mount--${controllerName}`, - `turbo-mount-${controllerName}` - ]; -} - -export const registerComponents = ({turboMount, components, controllers}: RegisterComponentsProps) => { - const controllerModules = controllers ? definitionsFromGlob(controllers) : []; - - for (const [componentPath, componentModule] of Object.entries(components)) { - const name = componentPath.replace(/\.\w*$/, "") - .replace(/^[.\/]*components\//, ''); - - const identifiers = identifierNames(name, turboMount); - - const controller = controllerModules.find(({identifier}) => identifiers.includes(identifier)); - const component = componentModule.default ?? componentModule; - - if (controller) { - turboMount.register(name, component, controller.controllerConstructor); - } else { - turboMount.register(name, component); - } + turboMount: TurboMount; + components: Record; + controllers?: Record; +}; + +const identifierNames = (name: string, turboMount: TurboMount) => { + const controllerName = camelToKebabCase(name); + const framework = turboMount.framework; + + return [ + `turbo-mount--${framework}--${controllerName}`, + `turbo-mount--${framework}-${controllerName}`, + `turbo-mount-${framework}-${controllerName}`, + `turbo-mount--${controllerName}`, + `turbo-mount-${controllerName}`, + ]; +}; + +export const registerComponents = ({ + turboMount, + components, + controllers, +}: RegisterComponentsProps) => { + const controllerModules = controllers ? definitionsFromGlob(controllers) : []; + + for (const [componentPath, componentModule] of Object.entries(components)) { + const name = componentPath + .replace(/\.\w*$/, "") + .replace(/^[./]*components\//, ""); + + const identifiers = identifierNames(name, turboMount); + + const controller = controllerModules.find(({ identifier }) => + identifiers.includes(identifier), + ); + const component = componentModule.default ?? componentModule; + + if (controller) { + turboMount.register(name, component, controller.controllerConstructor); + } else { + turboMount.register(name, component); } -} + } +};