From 2c2a6d5f34cd89fda14589cba4447355a080c65a Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Wed, 20 Dec 2023 20:22:35 +0000 Subject: [PATCH 1/6] test: add benchmark with vitest + codspeed --- .github/workflows/bench.yml | 25 ++++++++++++++ package.json | 1 + pnpm-lock.yaml | 49 ++++++++++++++++++++++++++- test/transform.bench.ts | 48 ++++++++++++++++++++++++++ vitest.config.ts => vitest.config.mts | 2 ++ 5 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/bench.yml create mode 100644 test/transform.bench.ts rename vitest.config.ts => vitest.config.mts (83%) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml new file mode 100644 index 00000000..b5579b9c --- /dev/null +++ b/.github/workflows/bench.yml @@ -0,0 +1,25 @@ +name: benchmark + +on: + push: + branches: + - main + pull_request: + +jobs: + run: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: pnpm/action-setup@v2 + - uses: actions/setup-node@v3 + with: + cache: pnpm + - name: Install dependencies + run: pnpm install + - name: Run benchmarks + uses: CodSpeedHQ/action@v2 + with: + run: pnpm vitest bench + # token retrieved from the CodSpeed app at the previous step + token: ${{ secrets.CODSPEED_TOKEN }} diff --git a/package.json b/package.json index 4bf80333..d10ff2f3 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "unplugin": "^1.5.0" }, "devDependencies": { + "@codspeed/vitest-plugin": "^2.3.1", "@nuxt/kit": "3.6.5", "@types/estree": "1.0.5", "@types/node": "20.10.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9dde5e81..8fc85f2d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,6 +30,9 @@ importers: specifier: ^1.5.0 version: 1.5.1 devDependencies: + '@codspeed/vitest-plugin': + specifier: ^2.3.1 + version: 2.3.1(vite@5.0.8)(vitest@0.34.6) '@nuxt/kit': specifier: 3.6.5 version: 3.6.5(rollup@4.8.0) @@ -453,6 +456,24 @@ packages: mime: 3.0.0 dev: false + /@codspeed/core@2.3.1: + resolution: {integrity: sha512-7KRwBX4iXK33gEQwh8jPWBF9srGIjewm3oc+A/66caiG/aOyHmxJCapjAZxT2f2vIVYqR7CghzqlxY2ik0DNBg==} + dependencies: + find-up: 6.3.0 + node-gyp-build: 4.7.0 + dev: true + + /@codspeed/vitest-plugin@2.3.1(vite@5.0.8)(vitest@0.34.6): + resolution: {integrity: sha512-/e4G2B/onX/hG/EjUU/NpDxnIryeTDamVRTBeWfgQDoex3g7GDzTwoQktaU5l/Asw3ZjEErQg+oQVToQ6jYZlA==} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 + vitest: '>=1.0.0-beta.4 || >=1' + dependencies: + '@codspeed/core': 2.3.1 + vite: 5.0.8(@types/node@20.10.3) + vitest: 0.34.6 + dev: true + /@csstools/cascade-layer-name-parser@1.0.5(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1): resolution: {integrity: sha512-v/5ODKNBMfBl0us/WQjlfsvSlYxfZLhNMVIsuCPib2ulTwGKYbKJbwqw671+qH9Y4wvWVnu7LBChvml/wBKjFg==} engines: {node: ^14 || ^16 || >=18} @@ -4788,6 +4809,14 @@ packages: locate-path: 6.0.0 path-exists: 4.0.0 + /find-up@6.3.0: + resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + dev: true + /flat-cache@3.2.0: resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} engines: {node: ^10.12.0 || >=12.0.0} @@ -5913,6 +5942,13 @@ packages: dependencies: p-locate: 5.0.0 + /locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + p-locate: 6.0.0 + dev: true + /lodash._reinterpolate@3.0.0: resolution: {integrity: sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==} dev: false @@ -7052,7 +7088,6 @@ packages: /node-gyp-build@4.7.0: resolution: {integrity: sha512-PbZERfeFdrHQOOXiAKOY0VPbykZy90ndPKk0d+CFDegTKmWp1VgOTz2xACVbr1BjCWxrQp68CXtvNsveFhqDJg==} hasBin: true - dev: false /node-gyp@10.0.1: resolution: {integrity: sha512-gg3/bHehQfZivQVfqIyy8wTdSymF9yTyP4CJifK73imyNMU8AIGQE2pUa7dNWfmMeG9cDVF2eehiRMv0LC1iAg==} @@ -7484,6 +7519,13 @@ packages: dependencies: p-limit: 3.1.0 + /p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + p-limit: 4.0.0 + dev: true + /p-map@4.0.0: resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} engines: {node: '>=10'} @@ -7611,6 +7653,11 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + /path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + /path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} diff --git a/test/transform.bench.ts b/test/transform.bench.ts new file mode 100644 index 00000000..79781d82 --- /dev/null +++ b/test/transform.bench.ts @@ -0,0 +1,48 @@ +import { bench, describe } from 'vitest' +import { parse } from 'acorn' + +import { MagicRegExpTransformPlugin } from '../src/transform' + +const testCases = [ + [ + `import { createRegExp, exactly, anyOf } from 'magic-regexp'`, + "const re1 = createRegExp(exactly('bar').notBefore('foo'))", + ], + "const re1 = createRegExp(exactly('bar').notBefore('foo'))", + [`// magic-regexp`, "const re1 = createRegExp(exactly('bar').notBefore('foo'))"], + [ + "import { something } from 'other-module'", + `import { createRegExp, exactly, anyOf } from 'magic-regexp'`, + '//', // this lets us tree-shake the import for use in our test-suite + "const re1 = createRegExp(exactly('bar').notBefore('foo'))", + "const re2 = createRegExp(anyOf(exactly('bar'), 'foo'))", + "const re3 = createRegExp('/foo/bar')", + // This line will be double-escaped in the snapshot + "re3.test('/foo/bar')", + ], + [ + `import { createRegExp as cRE } from 'magic-regexp'`, + `import { exactly as ext, createRegExp } from 'magic-regexp'`, + `import * as magicRE from 'magic-regexp'`, + "const re1 = cRE(ext('bar').notBefore('foo'))", + "const re2 = magicRE.createRegExp(magicRE.anyOf('bar', 'foo'))", + "const re3 = createRegExp('test/value')", + ], +] + +describe(`transformer: magic-regexp`, () => { + bench('transformer: magic-regexp', () => { + for (const testCase of testCases) { + transform(testCase) + } + }) +}) + +const transform = (code: string | string[], id = 'some-id.js') => { + const plugin = MagicRegExpTransformPlugin.vite() as any + return plugin.transform.call( + { parse: (code: string) => parse(code, { ecmaVersion: 2022, sourceType: 'module' }) }, + Array.isArray(code) ? code.join('\n') : code, + id + )?.code +} diff --git a/vitest.config.ts b/vitest.config.mts similarity index 83% rename from vitest.config.ts rename to vitest.config.mts index e9743713..224d2a22 100644 --- a/vitest.config.ts +++ b/vitest.config.mts @@ -1,7 +1,9 @@ import { fileURLToPath } from 'node:url' +import codspeedPlugin from '@codspeed/vitest-plugin' import { defineConfig } from 'vitest/config' export default defineConfig({ + plugins: [codspeedPlugin()], resolve: { alias: { 'magic-regexp': fileURLToPath(new URL('./src/index.ts', import.meta.url).href), From aa94d6fd89ca09c78722fb5602d8824d54e3ccd4 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Wed, 20 Dec 2023 20:24:36 +0000 Subject: [PATCH 2/6] ci: add workflow dispatch target --- .github/workflows/bench.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index b5579b9c..df7f143a 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -5,6 +5,7 @@ on: branches: - main pull_request: + workflow_dispatch: jobs: run: From 44b4b56cae2847a0aab122e616bd827040895dbc Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Wed, 20 Dec 2023 20:34:25 +0000 Subject: [PATCH 3/6] chore: update lockfile --- pnpm-lock.yaml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8fc85f2d..3a3a8a34 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,7 +32,7 @@ importers: devDependencies: '@codspeed/vitest-plugin': specifier: ^2.3.1 - version: 2.3.1(vite@5.0.8)(vitest@0.34.6) + version: 2.3.1(vite@5.0.8)(vitest@1.1.0) '@nuxt/kit': specifier: 3.6.5 version: 3.6.5(rollup@4.8.0) @@ -463,7 +463,7 @@ packages: node-gyp-build: 4.7.0 dev: true - /@codspeed/vitest-plugin@2.3.1(vite@5.0.8)(vitest@0.34.6): + /@codspeed/vitest-plugin@2.3.1(vite@5.0.8)(vitest@1.1.0): resolution: {integrity: sha512-/e4G2B/onX/hG/EjUU/NpDxnIryeTDamVRTBeWfgQDoex3g7GDzTwoQktaU5l/Asw3ZjEErQg+oQVToQ6jYZlA==} peerDependencies: vite: ^4.2.0 || ^5.0.0 @@ -471,7 +471,7 @@ packages: dependencies: '@codspeed/core': 2.3.1 vite: 5.0.8(@types/node@20.10.3) - vitest: 0.34.6 + vitest: 1.1.0(@types/node@20.10.3) dev: true /@csstools/cascade-layer-name-parser@1.0.5(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1): @@ -7506,6 +7506,13 @@ packages: dependencies: yocto-queue: 0.1.0 + /p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + yocto-queue: 1.0.0 + dev: true + /p-limit@5.0.0: resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} engines: {node: '>=18'} From 02f68c8499f36c44e6bbe5575e0a4f829152b159 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Wed, 20 Dec 2023 20:36:39 +0000 Subject: [PATCH 4/6] refactor: split out separate benchmarks --- test/transform.bench.ts | 76 +++++++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/test/transform.bench.ts b/test/transform.bench.ts index 79781d82..681cf987 100644 --- a/test/transform.bench.ts +++ b/test/transform.bench.ts @@ -3,38 +3,56 @@ import { parse } from 'acorn' import { MagicRegExpTransformPlugin } from '../src/transform' -const testCases = [ - [ - `import { createRegExp, exactly, anyOf } from 'magic-regexp'`, - "const re1 = createRegExp(exactly('bar').notBefore('foo'))", - ], - "const re1 = createRegExp(exactly('bar').notBefore('foo'))", - [`// magic-regexp`, "const re1 = createRegExp(exactly('bar').notBefore('foo'))"], - [ - "import { something } from 'other-module'", +describe(`transformer: magic-regexp`, () => { + const couldTransform = [ `import { createRegExp, exactly, anyOf } from 'magic-regexp'`, - '//', // this lets us tree-shake the import for use in our test-suite "const re1 = createRegExp(exactly('bar').notBefore('foo'))", - "const re2 = createRegExp(anyOf(exactly('bar'), 'foo'))", - "const re3 = createRegExp('/foo/bar')", - // This line will be double-escaped in the snapshot - "re3.test('/foo/bar')", - ], - [ - `import { createRegExp as cRE } from 'magic-regexp'`, - `import { exactly as ext, createRegExp } from 'magic-regexp'`, - `import * as magicRE from 'magic-regexp'`, - "const re1 = cRE(ext('bar').notBefore('foo'))", - "const re2 = magicRE.createRegExp(magicRE.anyOf('bar', 'foo'))", - "const re3 = createRegExp('test/value')", - ], -] + ] -describe(`transformer: magic-regexp`, () => { - bench('transformer: magic-regexp', () => { - for (const testCase of testCases) { - transform(testCase) - } + bench('ignores non-JS files', () => { + transform(couldTransform, 'test.css') + }) + + bench('transforms vue script blocks', () => { + transform(couldTransform, 'test.vue?type=script') + transform(couldTransform, 'test.vue') + transform(couldTransform, 'test.vue?type=template') + }) + + bench(`ignores code without imports from magic-regexp`, () => { + transform(couldTransform[1]) + transform([`// magic-regexp`, couldTransform[1]]) + }) + + bench('preserves context for dynamic regexps', () => { + transform([ + `import { createRegExp } from 'magic-regexp'`, + `console.log(createRegExp(anyOf(keys)))`, + ]) + }) + + bench('statically replaces regexps where possible', () => { + transform([ + "import { something } from 'other-module'", + `import { createRegExp, exactly, anyOf } from 'magic-regexp'`, + '//', // this lets us tree-shake the import for use in our test-suite + "const re1 = createRegExp(exactly('bar').notBefore('foo'))", + "const re2 = createRegExp(anyOf(exactly('bar'), 'foo'))", + "const re3 = createRegExp('/foo/bar')", + // This line will be double-escaped in the snapshot + "re3.test('/foo/bar')", + ]) + }) + + bench('respects how users import library', () => { + transform([ + `import { createRegExp as cRE } from 'magic-regexp'`, + `import { exactly as ext, createRegExp } from 'magic-regexp'`, + `import * as magicRE from 'magic-regexp'`, + "const re1 = cRE(ext('bar').notBefore('foo'))", + "const re2 = magicRE.createRegExp(magicRE.anyOf('bar', 'foo'))", + "const re3 = createRegExp('test/value')", + ]) }) }) From 03f739f57c2b1a83d9e7788aff66e45382e32bd5 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Sat, 24 Feb 2024 16:05:47 +0000 Subject: [PATCH 5/6] chore: add tea.yml (experimental) --- tea.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tea.yml diff --git a/tea.yml b/tea.yml new file mode 100644 index 00000000..f2d37b29 --- /dev/null +++ b/tea.yml @@ -0,0 +1,7 @@ +# https://tea.xyz/what-is-this-file +--- +version: 1.0.0 +codeOwners: + - '0xBb533C940878fdBa9E5434d659e05dAbEc4EC423' +quorum: 1 + From 95d9b361290998ac23e7c6ea2d8bc304b93428ff Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Fri, 22 Mar 2024 10:10:44 +0000 Subject: [PATCH 6/6] chore: revert tea --- tea.yml | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 tea.yml diff --git a/tea.yml b/tea.yml deleted file mode 100644 index f2d37b29..00000000 --- a/tea.yml +++ /dev/null @@ -1,7 +0,0 @@ -# https://tea.xyz/what-is-this-file ---- -version: 1.0.0 -codeOwners: - - '0xBb533C940878fdBa9E5434d659e05dAbEc4EC423' -quorum: 1 -