From c28b4c24e795af431f011bb981ce0a2742be6232 Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 9 Sep 2024 20:17:29 +0200 Subject: [PATCH] feat: 1.0.0 --- .github/workflows/deploy.yml | 39 ++++++++++++++++ .gitignore | 5 +++ README.md | 2 +- package.json | 61 +++++++++++++++++++++++++ src/core/helpers/out.helper.ts | 32 +++++++++++++ src/core/plugin.ts | 82 ++++++++++++++++++++++++++++++++++ src/core/types/options.type.ts | 30 +++++++++++++ src/public-api.ts | 2 + tsconfig.json | 18 ++++++++ 9 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/deploy.yml create mode 100644 .gitignore create mode 100644 package.json create mode 100644 src/core/helpers/out.helper.ts create mode 100644 src/core/plugin.ts create mode 100644 src/core/types/options.type.ts create mode 100644 src/public-api.ts create mode 100644 tsconfig.json diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..e417e24 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,39 @@ +name: Deploy +on: + push: + branches: + - release + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install + uses: pnpm/action-setup@v4 + with: + version: latest + run_install: true + + - name: Build + run: tsc + + - name: Assets + run: | + cp package.json dist/package.json + cp README.md dist/README.md + + - name: Config + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + run: | + cd dist + echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc + echo "registry=https://registry.npmjs.org/" >> ~/.npmrc + + - name: Publish + run: | + cd dist + pnpm publish --access public --no-git-checks diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..690e77a --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules +.idea +package-lock.json +pnpm-lock.yaml +dist diff --git a/README.md b/README.md index 510823d..7aaf24f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ -# esbuild-plugin-copy +# esbuild-plugin-copy-it The plugin copies assets to the esbuild out folder, ensuring that all required files are included alongside the built code diff --git a/package.json b/package.json new file mode 100644 index 0000000..ad2ae61 --- /dev/null +++ b/package.json @@ -0,0 +1,61 @@ +{ + "name": "esbuild-plugin-file-copy", + "description": "The plugin copies assets to the esbuild out folder, ensuring that all required files are included alongside the built code.", + "keywords": [ + "frontend", + "backend", + "packages", + "esbuild", + "build", + "esbuild plugin", + "esbuild tool", + "build plugin", + "build tool", + "package.json", + "developer tools", + "build process", + "plugin", + "esbuild dependency", + "dependency management", + "optimize package json", + "validate package json", + "esbuild copy package json", + "esbuild copy", + "package json plugin", + "package config plugin" + ], + "license": "MIT", + "version": "1.0.1", + "bugs": "https://github.com/simonkovtyk/esbuild-plugin-file-copy/issues", + "homepage": "https://github.com/simonkovtyk/esbuild-plugin-file-copy", + "repository": { + "type": "git", + "url": "git+https://github.com/simonkovtyk/esbuild-plugin-file-copy.git" + }, + "exports": { + ".": { + "default": "./public-api.js", + "types": "./public-api.d.ts" + } + }, + "author": { + "name": "Simon Kovtyk", + "url": "https://github.com/simonkovtyk", + "email": "privat@kovtyk.com" + }, + "maintainers": [ + { + "name": "Simon Kovtyk", + "url": "https://github.com/simonkovtyk", + "email": "privat@kovtyk.com" + } + ], + "dependencies": { + "esbuild": ">=0.20.0", + "fast-glob": "^3.3.2" + }, + "devDependencies": { + "typescript": "^5.5.4", + "@types/node": "^22.5.4" + } +} diff --git a/src/core/helpers/out.helper.ts b/src/core/helpers/out.helper.ts new file mode 100644 index 0000000..47d2be8 --- /dev/null +++ b/src/core/helpers/out.helper.ts @@ -0,0 +1,32 @@ +import path from "node:path"; +import process from "node:process"; +import { ResolvePathOptions } from "../types/options.type"; + +// Prefer out dir before out file +const resolveOutDir = (options: ResolvePathOptions): string => { + const explicitOutBase: string | undefined = options.overrideOutBase ?? options.outBase; + + const outBase = explicitOutBase + ? path.join(process.cwd(), explicitOutBase) + : process.cwd(); + + const explicitOutDir: string | undefined = options.overrideOutDir ?? options.outDir; + + if (explicitOutDir !== undefined) { + return path.join(outBase, explicitOutDir); + } + + const explicitOutFile: string | undefined = options.overrideOutFile ?? options.outFile; + + if (explicitOutFile !== undefined) { + const dirOfOutFile = path.parse(explicitOutFile).dir; + + return path.join(outBase, dirOfOutFile); + } + + return path.join(outBase, "dist") +} + +export { + resolveOutDir +} diff --git a/src/core/plugin.ts b/src/core/plugin.ts new file mode 100644 index 0000000..9cd07b6 --- /dev/null +++ b/src/core/plugin.ts @@ -0,0 +1,82 @@ +import path from "node:path"; +import { resolveOutDir } from "./helpers/out.helper"; +import { Input, Lifecycle, Options, ResolvePathOptions } from "./types/options.type"; +import { PluginBuild, Plugin } from "esbuild"; +import fastGlob from "fast-glob" +import fs from "node:fs"; + +const handler = (inputs: string[] | Input[], options: ResolvePathOptions) => { + return async () => { + const resolvedOutDir: string = resolveOutDir(options); + + inputs.forEach((input: string | Input): void => { + if (typeof input === "string") { + const globs: string[] = fastGlob.sync(input); + + if (globs.length === 0) + return; + + if (! fs.existsSync(resolvedOutDir)) + fs.mkdirSync(resolvedOutDir, { recursive: true }); + + globs.forEach((glob: string): void => { + const fileName: string = path.parse(glob).base; + + fs.copyFileSync(glob, `${ resolvedOutDir }/${ fileName }`); + }); + + return; + } + + const globs: string[] = fastGlob.sync(input.glob); + + if (globs.length === 0) + return; + + globs.forEach((glob: string): void => { + const fileName: string = path.parse(glob).base; + + const outDir: string = input.output ?? resolvedOutDir; + + if (! fs.existsSync(outDir)) + fs.mkdirSync(outDir, { recursive: true }); + + fs.copyFileSync(glob, `${ outDir }/${ fileName }`); + }) + }); + } +} + +const copyPlugin = (options: Options): Plugin => ({ + name: "esbuild-plugin-package-json", + setup: (build: PluginBuild) => { + const lifecycle: Lifecycle = options.lifecycle ?? "onEnd"; + + const resolvePathOptions: ResolvePathOptions = { + outBase: build.initialOptions.outbase, + outDir: build.initialOptions.outdir, + outFile: build.initialOptions.outfile, + overrideOutBase: options.overrideOutBase, + overrideOutDir: options.overrideOutDir, + overrideOutFile: options.overrideOutFile + } + + const handlerRef = handler(options.inputs, resolvePathOptions); + + switch (lifecycle) { + case "onStart": + build.onStart(handlerRef); + break; + case "onEnd": + build.onEnd(handlerRef); + break; + case "onDispose": + build.onDispose(handlerRef); + break; + } + } +}) + +export { + copyPlugin +} diff --git a/src/core/types/options.type.ts b/src/core/types/options.type.ts new file mode 100644 index 0000000..2d6470d --- /dev/null +++ b/src/core/types/options.type.ts @@ -0,0 +1,30 @@ +type Lifecycle = "onStart" | "onEnd" | "onDispose"; + +type Input = { + glob: string, + output?: string | undefined +} + +type PathOverrides = { + overrideOutBase?: string | undefined, + overrideOutDir?: string | undefined, + overrideOutFile?: string | undefined +} + +type Options = { + lifecycle?: Lifecycle | undefined, + inputs: string[] | Input[] +} & PathOverrides + +type ResolvePathOptions = { + outDir?: string | undefined, + outFile?: string | undefined, + outBase?: string | undefined +} & PathOverrides + +export type { + ResolvePathOptions, + Lifecycle, + Options, + Input +} diff --git a/src/public-api.ts b/src/public-api.ts new file mode 100644 index 0000000..aa40484 --- /dev/null +++ b/src/public-api.ts @@ -0,0 +1,2 @@ +export * from "./core/plugin"; +export * from "./core/types/options.type"; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2d36a70 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "outDir": "dist", + "module": "NodeNext", + "target": "ESNext", + "moduleResolution": "NodeNext", + "allowSyntheticDefaultImports": true, + "strict": true, + "noImplicitAny": true, + "noImplicitThis": true, + "noImplicitReturns": true, + "noImplicitOverride": true, + "noUncheckedIndexedAccess": true, + "declaration": true, + "alwaysStrict": true, + "isolatedModules": true + } +}