diff --git a/.eslintignore b/.eslintignore index 376fc595..c14493df 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,4 @@ lib esm umd -.eslintrc.js +eslint.config.js diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 1ecda947..00000000 --- a/.eslintrc.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - env: { - "jest/globals": true, - browser: true, - es6: true, - node: true, - }, - extends: [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "prettier", - ], - parser: "@typescript-eslint/parser", - parserOptions: { - ecmaVersion: 2018, - sourceType: "module", - project: "./tsconfig.eslint.json", - }, - plugins: ["jest"], -}; diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 00000000..7df0f0f5 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,34 @@ +import globals from "globals"; +import eslint from "@eslint/js"; +import tsEslint from "typescript-eslint"; +import eslintConfigPrettier from "eslint-config-prettier"; +import eslintPluginJest from "eslint-plugin-jest"; + +/** @type {import("eslint").Linter.FlatConfig[]} */ +export default tsEslint.config( + { + ignores: ["lib/**", "esm/**", "umd/**"], + extends: [ + eslint.configs.recommended, + ...tsEslint.configs.recommended, + eslintConfigPrettier, + ], + languageOptions: { + globals: { + ...globals.jest, + }, + parser: tsEslint.parser, + parserOptions: { + sourceType: "module", + project: "./tsconfig.eslint.json", + }, + }, + plugins: { + "@typescript-eslint": tsEslint.plugin, + }, + }, + { + files: ["**/tests/**/*.test.ts"], + ...eslintPluginJest.configs["flat/recommended"], + }, +); diff --git a/jest.config.js b/jest.config.mjs similarity index 94% rename from jest.config.js rename to jest.config.mjs index 0e2355d4..43b71f0d 100644 --- a/jest.config.js +++ b/jest.config.mjs @@ -1,4 +1,4 @@ -module.exports = { +export default { moduleFileExtensions: ["ts", "js"], transform: { "^.+\\.(ts|tsx)$": "ts-jest", diff --git a/package-lock.json b/package-lock.json index cc756cfa..edbe4576 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,10 +12,9 @@ "@babel/core": "7.24.7", "@babel/preset-env": "7.24.7", "@types/core-js": "2.5.8", + "@types/eslint": "8.56.10", "@types/jest": "29.5.12", "@types/node": "20.14.10", - "@typescript-eslint/eslint-plugin": "7.16.0", - "@typescript-eslint/parser": "7.16.0", "babel-loader": "9.1.3", "cspell": "8.10.4", "eslint": "8.57.0", @@ -28,6 +27,7 @@ "ts-jest": "29.2.2", "ts-loader": "9.5.1", "typescript": "5.5.3", + "typescript-eslint": "7.16.0", "webpack": "5.92.1", "webpack-cli": "5.1.4" } @@ -3373,9 +3373,9 @@ "dev": true }, "node_modules/@types/eslint": { - "version": "7.2.13", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.13.tgz", - "integrity": "sha512-LKmQCWAlnVHvvXq4oasNUMTJJb2GwSyTY8+1C7OH5ILR8mPLaljv1jxL1bXW3xB3jFbQxTKxJAvI8PyjB09aBg==", + "version": "8.56.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", + "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", "dev": true, "dependencies": { "@types/estree": "*", @@ -9825,6 +9825,32 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.16.0.tgz", + "integrity": "sha512-kaVRivQjOzuoCXU6+hLnjo3/baxyzWVO5GrnExkFzETRYJKVHYkrJglOu2OCm8Hi9RPDWX1PTNNTpU5KRV0+RA==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "7.16.0", + "@typescript-eslint/parser": "7.16.0", + "@typescript-eslint/utils": "7.16.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -12768,9 +12794,9 @@ "dev": true }, "@types/eslint": { - "version": "7.2.13", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.13.tgz", - "integrity": "sha512-LKmQCWAlnVHvvXq4oasNUMTJJb2GwSyTY8+1C7OH5ILR8mPLaljv1jxL1bXW3xB3jFbQxTKxJAvI8PyjB09aBg==", + "version": "8.56.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", + "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", "dev": true, "requires": { "@types/estree": "*", @@ -17462,6 +17488,17 @@ "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", "dev": true }, + "typescript-eslint": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.16.0.tgz", + "integrity": "sha512-kaVRivQjOzuoCXU6+hLnjo3/baxyzWVO5GrnExkFzETRYJKVHYkrJglOu2OCm8Hi9RPDWX1PTNNTpU5KRV0+RA==", + "dev": true, + "requires": { + "@typescript-eslint/eslint-plugin": "7.16.0", + "@typescript-eslint/parser": "7.16.0", + "@typescript-eslint/utils": "7.16.0" + } + }, "undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", diff --git a/package.json b/package.json index 40b15202..09715b06 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "@progfay/scrapbox-parser", "version": "9.0.0", + "type": "module", "description": "parse Scrapbox notation to JavaScript Object", "files": [ "lib", @@ -28,12 +29,12 @@ "test:update": "jest --updateSnapshot --no-cache", "lint": "run-p lint:*", "lint:prettier": "prettier --check .", - "lint:eslint": "eslint -c ./.eslintrc.js .", + "lint:eslint": "eslint .", "lint:cspell": "cspell --no-summary '**/*'", "lint:typescript": "tsc -p ./tsconfig.eslint.json", "format": "run-s format:prettier format:eslint", "format:prettier": "prettier --write .", - "format:eslint": "eslint --fix -c ./.eslintrc.js ." + "format:eslint": "eslint --fix ." }, "repository": { "type": "git", @@ -53,10 +54,9 @@ "@babel/core": "7.24.7", "@babel/preset-env": "7.24.7", "@types/core-js": "2.5.8", + "@types/eslint": "8.56.10", "@types/jest": "29.5.12", "@types/node": "20.14.10", - "@typescript-eslint/eslint-plugin": "7.16.0", - "@typescript-eslint/parser": "7.16.0", "babel-loader": "9.1.3", "cspell": "8.10.4", "eslint": "8.57.0", @@ -69,6 +69,7 @@ "ts-jest": "29.2.2", "ts-loader": "9.5.1", "typescript": "5.5.3", + "typescript-eslint": "7.16.0", "webpack": "5.92.1", "webpack-cli": "5.1.4" }, diff --git a/src/block/Pack.ts b/src/block/Pack.ts index 9a19ade6..bf0ad7bf 100644 --- a/src/block/Pack.ts +++ b/src/block/Pack.ts @@ -22,8 +22,8 @@ const packing = (packs: Pack[], row: Row): Pack[] => { type: /^\s*code:/.test(row.text) ? "codeBlock" : /^\s*table:/.test(row.text) - ? "table" - : "line", + ? "table" + : "line", rows: [row], }); diff --git a/src/block/Table.ts b/src/block/Table.ts index a731ab59..4204f14a 100644 --- a/src/block/Table.ts +++ b/src/block/Table.ts @@ -37,8 +37,8 @@ export const convertToTable = (pack: TablePack): Table => { nested: false, quoted: false, context: "table", - }) - ) + }), + ), ), }; }; diff --git a/src/block/node/CommandLineNode.ts b/src/block/node/CommandLineNode.ts index 61546de8..0969fbbe 100644 --- a/src/block/node/CommandLineNode.ts +++ b/src/block/node/CommandLineNode.ts @@ -9,7 +9,7 @@ const commandLineRegExp = /^[$%] .+$/; const createCommandLineNode: NodeCreator = ( raw: string, - opts + opts, ) => { if (opts.context === "table") { return createPlainNode(raw, opts); @@ -34,5 +34,5 @@ export const CommandLineNodeParser: NodeParser = createNodeParser( parseOnNested: false, parseOnQuoted: false, patterns: [commandLineRegExp], - } + }, ); diff --git a/src/block/node/DecorationNode.ts b/src/block/node/DecorationNode.ts index 419b4120..cc8334fe 100644 --- a/src/block/node/DecorationNode.ts +++ b/src/block/node/DecorationNode.ts @@ -50,7 +50,7 @@ export type Decoration = Exclude | AsteriskDecorationChar; const createDecorationNode: NodeCreator = ( raw, - opts + opts, ) => { if (opts.context === "table") { return createPlainNode(raw, opts); @@ -84,5 +84,5 @@ export const DecorationNodeParser: NodeParser = createNodeParser( parseOnNested: false, parseOnQuoted: true, patterns: [decorationRegExp], - } + }, ); diff --git a/src/block/node/ExternalLinkNode.ts b/src/block/node/ExternalLinkNode.ts index 11b6af0e..ef9d3ad1 100644 --- a/src/block/node/ExternalLinkNode.ts +++ b/src/block/node/ExternalLinkNode.ts @@ -12,7 +12,7 @@ const httpRegExp = /https?:\/\/[^\s]+/; const createExternalLinkNode: NodeCreator = ( raw, - opts + opts, ) => { if (opts.context === "table") { return createPlainNode(raw, opts); @@ -55,5 +55,5 @@ export const ExternalLinkNodeParser: NodeParser = createNodeParser( bracketedUrlRegExp, httpRegExp, ], - } + }, ); diff --git a/src/block/node/GoogleMapNode.ts b/src/block/node/GoogleMapNode.ts index d03c74d1..404317d3 100644 --- a/src/block/node/GoogleMapNode.ts +++ b/src/block/node/GoogleMapNode.ts @@ -25,7 +25,7 @@ const parseCoordinate: (format: string) => Coordinate = (format) => { const createGoogleMapNode: NodeCreator = ( raw, - opts + opts, ) => { if (opts.context === "table") { return createPlainNode(raw, opts); @@ -45,7 +45,7 @@ const createGoogleMapNode: NodeCreator = ( const url = place !== "" ? `https://www.google.com/maps/place/${encodeURIComponent( - place + place, )}/@${latitude},${longitude},${zoom}z` : `https://www.google.com/maps/@${latitude},${longitude},${zoom}z`; diff --git a/src/block/node/HelpfeelNode.ts b/src/block/node/HelpfeelNode.ts index c29c7478..e2d83fa1 100644 --- a/src/block/node/HelpfeelNode.ts +++ b/src/block/node/HelpfeelNode.ts @@ -6,7 +6,10 @@ import type { NodeCreator } from "./creator"; const helpfeelRegExp = /^\? .+$/; -const createHelpfeelNode: NodeCreator = (raw, opts) => +const createHelpfeelNode: NodeCreator = ( + raw, + opts, +) => opts.context === "table" ? createPlainNode(raw, opts) : [ diff --git a/src/block/node/IconNode.ts b/src/block/node/IconNode.ts index 3ee29589..e2e608a9 100644 --- a/src/block/node/IconNode.ts +++ b/src/block/node/IconNode.ts @@ -7,13 +7,13 @@ import type { NodeCreator } from "./creator"; const iconRegExp = /\[[^[\]]*\.icon(?:\*[1-9]\d*)?\]/; export function generateIconNodeCreator( - type: IconNode["type"] + type: IconNode["type"], ): NodeCreator; export function generateIconNodeCreator( - type: StrongIconNode["type"] + type: StrongIconNode["type"], ): NodeCreator; export function generateIconNodeCreator( - type: (IconNode | StrongIconNode)["type"] + type: (IconNode | StrongIconNode)["type"], ): NodeCreator { return (raw, opts) => { if (type === "strongIcon" && opts.context === "table") { diff --git a/src/block/node/NumberListNode.ts b/src/block/node/NumberListNode.ts index ce6f7e3d..80d72b66 100644 --- a/src/block/node/NumberListNode.ts +++ b/src/block/node/NumberListNode.ts @@ -9,7 +9,7 @@ const numberListRegExp = /^[0-9]+\. .*$/; const createNumberListNode: NodeCreator = ( raw, - opts + opts, ) => { if (opts.context === "table") { return createPlainNode(raw, opts); diff --git a/src/block/node/StrongImageNode.ts b/src/block/node/StrongImageNode.ts index a0016491..b4fbc12a 100644 --- a/src/block/node/StrongImageNode.ts +++ b/src/block/node/StrongImageNode.ts @@ -10,7 +10,7 @@ const strongGyazoImageRegExp = const createStrongImageNode: NodeCreator = ( raw, - opts + opts, ) => { if (opts.context === "table") { return createPlainNode(raw, opts); diff --git a/src/block/node/creator.ts b/src/block/node/creator.ts index 6fb30af8..76a5c891 100644 --- a/src/block/node/creator.ts +++ b/src/block/node/creator.ts @@ -5,17 +5,17 @@ import type { Node } from "./type"; export type NodeCreator = ( target: string, - opts: NodeParserOption + opts: NodeParserOption, ) => T[]; type NodeParserCreator = ( nodeCreator: NodeCreator, - opts: { parseOnNested: boolean; parseOnQuoted: boolean; patterns: RegExp[] } + opts: { parseOnNested: boolean; parseOnQuoted: boolean; patterns: RegExp[] }, ) => NodeParser; export const createNodeParser: NodeParserCreator = ( nodeCreator, - { parseOnNested, parseOnQuoted, patterns } + { parseOnNested, parseOnQuoted, patterns }, ) => { return (text, opts, next) => { if (!parseOnNested && opts.nested) return next?.() ?? []; diff --git a/src/block/node/index.ts b/src/block/node/index.ts index 0d42d7ad..adfa53a0 100644 --- a/src/block/node/index.ts +++ b/src/block/node/index.ts @@ -28,7 +28,7 @@ export type NextNodeParser = () => Node[]; export type NodeParser = ( text: string, opts: NodeParserOption, - next?: NextNodeParser + next?: NextNodeParser, ) => Node[]; const FalsyEliminator: NodeParser = (text, _, next) => { @@ -43,7 +43,7 @@ const combineNodeParsers = (acc: NextNodeParser, parser: NodeParser): NextNodeParser => () => parser(text, opts, acc), - () => PlainNodeParser(text, opts) + () => PlainNodeParser(text, opts), )(); export const convertToNodes: ReturnType = @@ -65,5 +65,5 @@ export const convertToNodes: ReturnType = GoogleMapNodeParser, InternalLinkNodeParser, HashTagNodeParser, - NumberListNodeParser + NumberListNodeParser, ); diff --git a/tests/jest-setup.ts b/tests/jest-setup.ts index 04259edb..fb0692b6 100644 --- a/tests/jest-setup.ts +++ b/tests/jest-setup.ts @@ -23,7 +23,7 @@ expect.extend({ // eslint-disable-next-line @typescript-eslint/no-explicit-any this: any, received: string, - opts?: ParserOption + opts?: ParserOption, ) { const blocks = parse(received, opts); return toMatchSnapshot.call(this, blocks); diff --git a/tests/line/image.test.ts b/tests/line/image.test.ts index d176311d..691c9d52 100644 --- a/tests/line/image.test.ts +++ b/tests/line/image.test.ts @@ -8,7 +8,7 @@ describe("image", () => { it("HTTP jpeg image with special and japanese chars", () => { expect( - "[http://example.com/~!@#$%^&*()_+`-={}\\'\"?,.<>|/画像.jpeg]" + "[http://example.com/~!@#$%^&*()_+`-={}\\'\"?,.<>|/画像.jpeg]", ).toMatchSnapshotWhenParsing({ hasTitle: false }); }); @@ -17,13 +17,13 @@ describe("image", () => { [https://example.com/ https://example.com/image.GIF]`).toMatchSnapshotWhenParsing( { hasTitle: false, - } + }, ); }); it("Image with double image link", () => { expect( - "[https://example.com/forward.png https://example.com/backward.png]" + "[https://example.com/forward.png https://example.com/backward.png]", ).toMatchSnapshotWhenParsing({ hasTitle: false }); }); @@ -33,7 +33,7 @@ describe("image", () => { [https://gyazo.com/0f82099330f378fe4917a1b4a5fe8815/raw]`).toMatchSnapshotWhenParsing( { hasTitle: false, - } + }, ); }); @@ -41,13 +41,13 @@ describe("image", () => { expect(`[https://gyazo.com/0f82099330f378fe4917a1b4a5fe8815 https://example.com] [https://example.com https://gyazo.com/0f82099330f378fe4917a1b4a5fe8815] [https://gyazo.com/7057219f5b20ca8afd122945b72453d3 https://gyazo.com/0f82099330f378fe4917a1b4a5fe8815]`).toMatchSnapshotWhenParsing( - { hasTitle: false } + { hasTitle: false }, ); }); it("Image with GET parameters", () => { expect( - "[http://example.com/image.png?key1=value1&key2=value2]" + "[http://example.com/image.png?key1=value1&key2=value2]", ).toMatchSnapshotWhenParsing({ hasTitle: false, }); @@ -55,7 +55,7 @@ describe("image", () => { it("Direct Gyazo image", () => { expect( - "[https://i.gyazo.com/0f82099330f378fe4917a1b4a5fe8815.png]" + "[https://i.gyazo.com/0f82099330f378fe4917a1b4a5fe8815.png]", ).toMatchSnapshotWhenParsing({ hasTitle: false }); }); }); diff --git a/tests/line/link.test.ts b/tests/line/link.test.ts index f938099e..e7d996a6 100644 --- a/tests/line/link.test.ts +++ b/tests/line/link.test.ts @@ -40,7 +40,7 @@ describe("link", () => { it("Link with link", () => { expect( - "[https://example.com https://example.com]" + "[https://example.com https://example.com]", ).toMatchSnapshotWhenParsing({ hasTitle: false, }); @@ -48,7 +48,7 @@ describe("link", () => { it("Link with GET parameters", () => { expect( - "[http://example.com?key1=value1&key2=value2]" + "[http://example.com?key1=value1&key2=value2]", ).toMatchSnapshotWhenParsing({ hasTitle: false, }); diff --git a/tests/line/strongImage.test.ts b/tests/line/strongImage.test.ts index 04dcdc13..120e334c 100644 --- a/tests/line/strongImage.test.ts +++ b/tests/line/strongImage.test.ts @@ -10,13 +10,13 @@ describe("strongImage", () => { it("HTTP jpeg strong image with special and japanese chars", () => { expect( - "[[http://example.com/~!@#$%^&*()_+`-={}\\'\"?,.<>|/画像.jpeg]]" + "[[http://example.com/~!@#$%^&*()_+`-={}\\'\"?,.<>|/画像.jpeg]]", ).toMatchSnapshotWhenParsing({ hasTitle: false }); }); it("Gyazo image", () => { expect( - "[[https://gyazo.com/0f82099330f378fe4917a1b4a5fe8815]]" + "[[https://gyazo.com/0f82099330f378fe4917a1b4a5fe8815]]", ).toMatchSnapshotWhenParsing({ hasTitle: false, }); @@ -24,7 +24,7 @@ describe("strongImage", () => { it("Direct Gyazo image", () => { expect( - "[[https://i.gyazo.com/0f82099330f378fe4917a1b4a5fe8815.png]]" + "[[https://i.gyazo.com/0f82099330f378fe4917a1b4a5fe8815.png]]", ).toMatchSnapshotWhenParsing({ hasTitle: false }); }); }); diff --git a/webpack.config.js b/webpack.config.mjs similarity index 87% rename from webpack.config.js rename to webpack.config.mjs index f104a16d..22f64317 100644 --- a/webpack.config.js +++ b/webpack.config.mjs @@ -1,9 +1,11 @@ -module.exports = { +import path from "node:path"; + +export default { mode: "production", entry: "./src/index.ts", output: { // eslint-disable-next-line @typescript-eslint/no-var-requires - path: require("path").resolve(__dirname, "umd"), + path: path.resolve(import.meta.dirname, "umd"), filename: "scrapbox-parser.js", library: "ScrapboxParser", libraryTarget: "umd",