diff --git a/workspaces/download-leetcode-submissions/package.json b/workspaces/download-leetcode-submissions/package.json index 436bb723..7b9802d3 100644 --- a/workspaces/download-leetcode-submissions/package.json +++ b/workspaces/download-leetcode-submissions/package.json @@ -15,7 +15,7 @@ "type": "module", "exports": "./src/main.ts", "scripts": { - "build": "cross-env NODE_OPTIONS=\"--import tsx\" webpack && chmod +x dist/download-leetcode-submissions.cjs", + "build": "cross-env NODE_OPTIONS=\"--import tsx\" webpack", "format": "prettier --color --write .", "lint": "eslint --color --max-warnings=0 .", "start": "tsx src/main.ts", @@ -29,6 +29,7 @@ }, "devDependencies": { "@code-chronicles/eslint-config": "workspace:*", + "@code-chronicles/webpack-make-output-executable-plugin": "workspace:*", "@types/node": "22.7.4", "cross-env": "7.0.3", "eslint": "9.11.1", diff --git a/workspaces/download-leetcode-submissions/webpack.config.ts b/workspaces/download-leetcode-submissions/webpack.config.ts index 5d995d6b..9358182e 100644 --- a/workspaces/download-leetcode-submissions/webpack.config.ts +++ b/workspaces/download-leetcode-submissions/webpack.config.ts @@ -8,6 +8,8 @@ import { } from "webpack"; import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin"; +import { WebpackMakeOutputExecutablePlugin } from "@code-chronicles/webpack-make-output-executable-plugin"; + import { stripPrefix } from "@code-chronicles/util/stripPrefix"; import { stripPrefixOrThrow } from "@code-chronicles/util/stripPrefixOrThrow"; @@ -60,6 +62,8 @@ const config: Configuration = { entryOnly: true, }), + new WebpackMakeOutputExecutablePlugin(), + new ForkTsCheckerWebpackPlugin(), ], }; diff --git a/workspaces/fetch-leetcode-problem-list/package.json b/workspaces/fetch-leetcode-problem-list/package.json index 940fd992..e8545316 100644 --- a/workspaces/fetch-leetcode-problem-list/package.json +++ b/workspaces/fetch-leetcode-problem-list/package.json @@ -27,6 +27,7 @@ }, "devDependencies": { "@code-chronicles/eslint-config": "workspace:*", + "@code-chronicles/webpack-make-output-executable-plugin": "workspace:*", "@types/node": "22.7.4", "cross-env": "7.0.3", "eslint": "9.11.1", diff --git a/workspaces/fetch-leetcode-problem-list/webpack.config.ts b/workspaces/fetch-leetcode-problem-list/webpack.config.ts index cb731518..9358182e 100644 --- a/workspaces/fetch-leetcode-problem-list/webpack.config.ts +++ b/workspaces/fetch-leetcode-problem-list/webpack.config.ts @@ -1,49 +1,20 @@ -import { chmod } from "node:fs/promises"; import { builtinModules } from "node:module"; import path from "node:path"; import { BannerPlugin, - type Compiler, type Configuration, type ExternalItemFunctionData, } from "webpack"; import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin"; +import { WebpackMakeOutputExecutablePlugin } from "@code-chronicles/webpack-make-output-executable-plugin"; + import { stripPrefix } from "@code-chronicles/util/stripPrefix"; import { stripPrefixOrThrow } from "@code-chronicles/util/stripPrefixOrThrow"; import packageJson from "./package.json" with { type: "json" }; -class WebpackMakeOutputExecutablePlugin { - // eslint-disable-next-line class-methods-use-this -- This is the interface expected by webpack. - apply(compiler: Compiler): void { - compiler.hooks.afterEmit.tapPromise( - "WebpackMakeOutputExecutablePlugin", - async (compilation) => { - const promises: Promise[] = []; - - for (const chunk of compilation.chunks) { - if (!chunk.canBeInitial()) { - continue; - } - - for (const file of chunk.files) { - promises.push( - chmod( - path.join(compilation.outputOptions.path ?? ".", file), - 0o755, - ), - ); - } - } - - await Promise.all(promises); - }, - ); - } -} - const config: Configuration = { target: "node", entry: path.resolve(__dirname, packageJson.exports), diff --git a/workspaces/fetch-recent-accepted-leetcode-submissions/package.json b/workspaces/fetch-recent-accepted-leetcode-submissions/package.json index d6605d40..6ddc973f 100644 --- a/workspaces/fetch-recent-accepted-leetcode-submissions/package.json +++ b/workspaces/fetch-recent-accepted-leetcode-submissions/package.json @@ -15,7 +15,7 @@ "type": "module", "exports": "./src/main.ts", "scripts": { - "build": "cross-env NODE_OPTIONS=\"--import tsx\" webpack && chmod +x dist/fetch-recent-accepted-leetcode-submissions.cjs", + "build": "cross-env NODE_OPTIONS=\"--import tsx\" webpack", "format": "prettier --color --write .", "lint": "eslint --color --max-warnings=0 .", "start": "tsx src/main.ts", @@ -27,6 +27,7 @@ }, "devDependencies": { "@code-chronicles/eslint-config": "workspace:*", + "@code-chronicles/webpack-make-output-executable-plugin": "workspace:*", "@types/node": "22.7.4", "cross-env": "7.0.3", "eslint": "9.11.1", diff --git a/workspaces/fetch-recent-accepted-leetcode-submissions/webpack.config.ts b/workspaces/fetch-recent-accepted-leetcode-submissions/webpack.config.ts index 5d995d6b..9358182e 100644 --- a/workspaces/fetch-recent-accepted-leetcode-submissions/webpack.config.ts +++ b/workspaces/fetch-recent-accepted-leetcode-submissions/webpack.config.ts @@ -8,6 +8,8 @@ import { } from "webpack"; import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin"; +import { WebpackMakeOutputExecutablePlugin } from "@code-chronicles/webpack-make-output-executable-plugin"; + import { stripPrefix } from "@code-chronicles/util/stripPrefix"; import { stripPrefixOrThrow } from "@code-chronicles/util/stripPrefixOrThrow"; @@ -60,6 +62,8 @@ const config: Configuration = { entryOnly: true, }), + new WebpackMakeOutputExecutablePlugin(), + new ForkTsCheckerWebpackPlugin(), ], }; diff --git a/workspaces/post-leetcode-potd-to-discord/package.json b/workspaces/post-leetcode-potd-to-discord/package.json index ffdc2c8d..8dfcba09 100644 --- a/workspaces/post-leetcode-potd-to-discord/package.json +++ b/workspaces/post-leetcode-potd-to-discord/package.json @@ -15,7 +15,7 @@ "type": "module", "exports": "./src/main.ts", "scripts": { - "build": "cross-env NODE_OPTIONS=\"--import tsx\" webpack && chmod +x dist/post-leetcode-potd-to-discord.cjs", + "build": "cross-env NODE_OPTIONS=\"--import tsx\" webpack", "format": "prettier --color --write .", "lint": "eslint --color --max-warnings=0 .", "start": "tsx src/main.ts", @@ -33,6 +33,7 @@ }, "devDependencies": { "@code-chronicles/eslint-config": "workspace:*", + "@code-chronicles/webpack-make-output-executable-plugin": "workspace:*", "@types/invariant": "2.2.37", "@types/node": "22.7.4", "cross-env": "7.0.3", diff --git a/workspaces/post-leetcode-potd-to-discord/webpack.config.ts b/workspaces/post-leetcode-potd-to-discord/webpack.config.ts index 07be6c29..457f38f1 100644 --- a/workspaces/post-leetcode-potd-to-discord/webpack.config.ts +++ b/workspaces/post-leetcode-potd-to-discord/webpack.config.ts @@ -8,6 +8,8 @@ import { } from "webpack"; import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin"; +import { WebpackMakeOutputExecutablePlugin } from "@code-chronicles/webpack-make-output-executable-plugin"; + import { stripPrefix } from "@code-chronicles/util/stripPrefix"; import { stripPrefixOrThrow } from "@code-chronicles/util/stripPrefixOrThrow"; @@ -66,6 +68,8 @@ const config: Configuration = { entryOnly: true, }), + new WebpackMakeOutputExecutablePlugin(), + new ForkTsCheckerWebpackPlugin(), ], }; diff --git a/workspaces/webpack-make-output-executable-plugin/README.md b/workspaces/webpack-make-output-executable-plugin/README.md new file mode 100644 index 00000000..b0130a3c --- /dev/null +++ b/workspaces/webpack-make-output-executable-plugin/README.md @@ -0,0 +1,11 @@ +# @code-chronicles/webpack-make-output-executable-plugin + +A [webpack](https://webpack.js.org/) plugin that `chmod`s entry points to be executable files. + +## Development + +Like the rest of the [Code Chronicles Leetcode ecosystem](../../), this package is structured as a Node module, using [Yarn](https://yarnpkg.com/) as the package manager. + +You can install dependencies by running `yarn`, either in this package's directory, or in the repository root. The usual `yarn format`, `yarn lint`, and `yarn typecheck` scripts are available to aid in development and occasionally to annoy. + +See also the repository's general [development guide](../../DEVELOPMENT.md). diff --git a/workspaces/webpack-make-output-executable-plugin/eslint.config.js b/workspaces/webpack-make-output-executable-plugin/eslint.config.js new file mode 100644 index 00000000..2ff17444 --- /dev/null +++ b/workspaces/webpack-make-output-executable-plugin/eslint.config.js @@ -0,0 +1 @@ +export { default } from "@code-chronicles/eslint-config"; diff --git a/workspaces/webpack-make-output-executable-plugin/package.json b/workspaces/webpack-make-output-executable-plugin/package.json new file mode 100644 index 00000000..fedd1d77 --- /dev/null +++ b/workspaces/webpack-make-output-executable-plugin/package.json @@ -0,0 +1,31 @@ +{ + "name": "@code-chronicles/webpack-make-output-executable-plugin", + "version": "0.0.1", + "license": "MIT", + "private": false, + "repository": { + "type": "git", + "url": "https://github.com/code-chronicles-code/leetcode-curriculum.git", + "directory": "workspaces/webpack-make-output-executable-plugin" + }, + "author": { + "name": "Miorel-Lucian Palii", + "url": "https://github.com/miorel" + }, + "type": "module", + "exports": { + ".": "./src/WebpackMakeOutputExecutablePlugin.ts" + }, + "scripts": { + "format": "prettier --color --write .", + "lint": "eslint --color --max-warnings=0 .", + "typecheck": "tsc --pretty --project ." + }, + "devDependencies": { + "@code-chronicles/eslint-config": "workspace:*", + "@types/node": "22.7.4", + "eslint": "9.11.1", + "prettier": "3.3.3", + "typescript": "5.6.2" + } +} diff --git a/workspaces/webpack-make-output-executable-plugin/src/WebpackMakeOutputExecutablePlugin.ts b/workspaces/webpack-make-output-executable-plugin/src/WebpackMakeOutputExecutablePlugin.ts new file mode 100644 index 00000000..bc934e10 --- /dev/null +++ b/workspaces/webpack-make-output-executable-plugin/src/WebpackMakeOutputExecutablePlugin.ts @@ -0,0 +1,33 @@ +import { chmod } from "node:fs/promises"; +import path from "node:path"; + +import type { Compiler } from "webpack"; + +export class WebpackMakeOutputExecutablePlugin { + // eslint-disable-next-line class-methods-use-this -- This is the interface expected by webpack. + apply(compiler: Compiler): void { + compiler.hooks.afterEmit.tapPromise( + "WebpackMakeOutputExecutablePlugin", + async (compilation) => { + const promises: Promise[] = []; + + for (const chunk of compilation.chunks) { + if (!chunk.canBeInitial()) { + continue; + } + + for (const file of chunk.files) { + promises.push( + chmod( + path.join(compilation.outputOptions.path ?? ".", file), + 0o755, + ), + ); + } + } + + await Promise.all(promises); + }, + ); + } +} diff --git a/workspaces/webpack-make-output-executable-plugin/tsconfig.json b/workspaces/webpack-make-output-executable-plugin/tsconfig.json new file mode 100644 index 00000000..69839bc7 --- /dev/null +++ b/workspaces/webpack-make-output-executable-plugin/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tsconfig-base.json" +} diff --git a/yarn.config.cjs b/yarn.config.cjs index 7156d065..31595991 100644 --- a/yarn.config.cjs +++ b/yarn.config.cjs @@ -1,6 +1,7 @@ const { defineConfig } = require("@yarnpkg/types"); const NAME_PREFIX = "@code-chronicles/"; +const CWD_PREFIX = "workspaces/"; module.exports = defineConfig({ async constraints({ Yarn }) { @@ -14,13 +15,12 @@ module.exports = defineConfig({ for (const workspace of Yarn.workspaces()) { if (workspace.cwd === ".") { workspace.unset("name"); + } else if (!workspace.cwd.startsWith(CWD_PREFIX)) { + workspace.error( + `Workspace directory didn't start with the prefix ${JSON.stringify(CWD_PREFIX)}`, + ); } else { - const { name } = workspace.manifest; - if (typeof name !== "string" || !name.startsWith(NAME_PREFIX)) { - workspace.error( - `Repository name didn't start with the prefix ${JSON.stringify(NAME_PREFIX)}`, - ); - } + workspace.set(NAME_PREFIX + workspace.cwd.slice(CWD_PREFIX.length)); } } diff --git a/yarn.lock b/yarn.lock index 816431fe..f0e9952b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -505,6 +505,7 @@ __metadata: "@code-chronicles/eslint-config": "workspace:*" "@code-chronicles/leetcode-api": "workspace:*" "@code-chronicles/util": "workspace:*" + "@code-chronicles/webpack-make-output-executable-plugin": "workspace:*" "@types/node": "npm:22.7.4" cross-env: "npm:7.0.3" eslint: "npm:9.11.1" @@ -546,6 +547,7 @@ __metadata: "@code-chronicles/eslint-config": "workspace:*" "@code-chronicles/leetcode-api": "workspace:*" "@code-chronicles/util": "workspace:*" + "@code-chronicles/webpack-make-output-executable-plugin": "workspace:*" "@types/node": "npm:22.7.4" cross-env: "npm:7.0.3" eslint: "npm:9.11.1" @@ -566,6 +568,7 @@ __metadata: "@code-chronicles/eslint-config": "workspace:*" "@code-chronicles/leetcode-api": "workspace:*" "@code-chronicles/util": "workspace:*" + "@code-chronicles/webpack-make-output-executable-plugin": "workspace:*" "@types/node": "npm:22.7.4" cross-env: "npm:7.0.3" eslint: "npm:9.11.1" @@ -661,6 +664,7 @@ __metadata: "@code-chronicles/eslint-config": "workspace:*" "@code-chronicles/leetcode-api": "workspace:*" "@code-chronicles/util": "workspace:*" + "@code-chronicles/webpack-make-output-executable-plugin": "workspace:*" "@types/invariant": "npm:2.2.37" "@types/node": "npm:22.7.4" bufferutil: "npm:4.0.8" @@ -717,6 +721,18 @@ __metadata: languageName: unknown linkType: soft +"@code-chronicles/webpack-make-output-executable-plugin@workspace:*, @code-chronicles/webpack-make-output-executable-plugin@workspace:workspaces/webpack-make-output-executable-plugin": + version: 0.0.0-use.local + resolution: "@code-chronicles/webpack-make-output-executable-plugin@workspace:workspaces/webpack-make-output-executable-plugin" + dependencies: + "@code-chronicles/eslint-config": "workspace:*" + "@types/node": "npm:22.7.4" + eslint: "npm:9.11.1" + prettier: "npm:3.3.3" + typescript: "npm:5.6.2" + languageName: unknown + linkType: soft + "@discordjs/builders@npm:^1.8.2": version: 1.9.0 resolution: "@discordjs/builders@npm:1.9.0"