Move the e2e suite to Go and playwright-go (#2180)
* Move the e2e suite to Go and playwright-go The seven playwright tests in `frontend/e2e` become twenty in `e2e/`, a separate Go module driving the same browsers through playwright-go. The npm project, its lockfile entries, its prettier config and `Dockerfile.e2e` go with it, leaving `frontend/` a single-member workspace. The suite covers posting with markdown, replying and the nesting that implies, editing inside the deadline and the backend refusing one outside it, deleting, voting with the optimistic score observed mid-flight and rolled back on failure, changing the sort, collapse persistence across a reload, dev, anonymous and email sign-in end to end, the profile iframe, and the two scripts that render into the host page rather than the widget's own frame. The rendering tests run in chromium, firefox and webkit. The rest sign in, sign-in needs the dev oauth2 provider, and reaching that by name from the host is chromium-only, so they run there alone. `compose-e2e-test.yml` runs remark42, a second instance with a short edit window so that path does not need a five-minute test, and mailpit, which catches the email verification message the suite reads back. Everything binds to the loopback interface: the stack holds a known secret and an admin shared id, and `go test` can start it unattended. The tests run on the host rather than in a container. Three settings there exist for the tests rather than for realism. `REMARK_URL` uses a hostname because the dev oauth2 server binds whatever host it reads out of it, and a loopback bind inside a container cannot be published. `UPDATE_LIMIT` is raised because the default of 0.5/sec rejects any test posting twice in a row. The suite also paces its own `/auth/` calls, which are capped at 2/sec by a bare literal in `rest.go` rather than by a setting. Each test gets its own comment thread from a query string on the demo page, so nothing has to reset the database between runs. CI gains a vet and lint job for the module, since the build tag keeps it out of a plain `go test ./...`, and uploads a browser trace for any test that fails. `e2e/README.md` carries the rest: how to run it, what the stack is for, and the widget behaviour the assertions have to work around. * Update golangci-lint to 2.13.1 in the backend workflow The pin sat three minors behind what the linter installs locally, so CI checked the backend with an older set of rules than anyone running it by hand. 2.10.1 also fetches its config schema over the network on every `config verify`, which is a failure mode with no bearing on the code. Both targets are clean on 2.13.1, `backend/app` and the memory_store example.
This commit is contained in:
@@ -1,7 +1,3 @@
|
||||
/.vscode/
|
||||
/.idea/
|
||||
|
||||
# e2e tests arficats
|
||||
/e2e/playwright-report/
|
||||
/e2e/playwright/.cache/
|
||||
/e2e/test-results/
|
||||
|
||||
+1
-3
@@ -7,14 +7,12 @@ Non-obvious constraints in the frontend toolchain and widget. Read before bumpin
|
||||
CI staying green does **not** mean every pin is consistent — `.nvmrc` in particular is never read by CI, so it can silently drift. After changing the node or pnpm version, grep the whole repo and update every one of these, not just the ones CI exercises:
|
||||
|
||||
- `Dockerfile` (production image) — `FROM node:X-alpine` and `npm i -g pnpm@X.Y.Z`
|
||||
- `frontend/Dockerfile.e2e` — `FROM mcr.microsoft.com/playwright:vX.Y.Z-noble` **and** `corepack prepare pnpm@X.Y.Z`
|
||||
- `site/Dockerfile`, `site/Dockerfile.dev` — `FROM node:X-alpine` (site uses yarn, not pnpm)
|
||||
- `frontend/.nvmrc`, `site/.nvmrc` — not read by CI at all; only matters to a human running `nvm use` locally. This is the one that drifted unnoticed: it sat at `16` through the whole node-20 migration because nothing red ever pointed at it.
|
||||
- Every `package.json`'s `packageManager` field (`frontend/package.json`, `frontend/apps/remark42/package.json` — `frontend/e2e/package.json` has none) and `frontend/apps/remark42/package.json`'s `engines` block
|
||||
- Every `package.json`'s `packageManager` field (`frontend/package.json`, `frontend/apps/remark42/package.json`) and `frontend/apps/remark42/package.json`'s `engines` block
|
||||
- `pnpm/action-setup@vN` blocks in `.github/workflows/ci-frontend.yml` (5) and `release.yml` (2) — pin `version:` to the **exact** patch (e.g. `10.10.0`), matching `packageManager`, not just the major. A floating major here is silent in CI (it just resolves to whatever the latest patch is at run time) but breaks the "Dockerfile and CI use the same pnpm" guarantee.
|
||||
- `node:` matrices in `.github/workflows/ci-frontend.yml` (every entry, not just the first) and the `node-version:` values in `release.yml`
|
||||
- `site/package.json`'s `engines.node` and `engines.yarn` (site uses yarn, so its `packageManager` moves independently)
|
||||
- `frontend/e2e/package.json`'s `@playwright/test`/`playwright` versions must match `frontend/Dockerfile.e2e`'s base image tag exactly, or the e2e container's bundled browser revision mismatches what the npm package expects.
|
||||
|
||||
When bumping pnpm/node, also re-check `frontend/apps/remark42/package.json`'s `engines` field — it's separate from `packageManager` and won't update itself.
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
FROM mcr.microsoft.com/playwright:v1.61.1-noble
|
||||
|
||||
ENV CI true
|
||||
WORKDIR /frontend
|
||||
|
||||
COPY ./package.json ./pnpm-workspace.yaml ./pnpm-lock.yaml /frontend/
|
||||
COPY ./e2e/package.json /frontend/e2e/
|
||||
RUN corepack enable && corepack prepare pnpm@10.10.0 --activate && pnpm install
|
||||
|
||||
COPY ./e2e/playwright.config.ts /frontend/e2e/
|
||||
COPY ./e2e/tests /frontend/e2e/tests/
|
||||
|
||||
WORKDIR /frontend/e2e
|
||||
|
||||
CMD pnpm test
|
||||
@@ -1,4 +0,0 @@
|
||||
node_modules/
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/playwright/.cache/
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"name": "@remark42/tests",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "playwright test"
|
||||
},
|
||||
"author": "Paul Mineev <paul@mineev.me>",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.61.1",
|
||||
"@types/node": "^26.0.1",
|
||||
"nanoid": "^5.1.16",
|
||||
"playwright": "1.61.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
import type { PlaywrightTestConfig } from '@playwright/test'
|
||||
import { devices } from '@playwright/test'
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
* https://github.com/motdotla/dotenv
|
||||
*/
|
||||
// require('dotenv').config();
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
const config: PlaywrightTestConfig = {
|
||||
testDir: './tests',
|
||||
/* Maximum time one test can run for. */
|
||||
timeout: 30 * 1000,
|
||||
expect: {
|
||||
/**
|
||||
* Maximum time expect() should wait for the condition to be met.
|
||||
* For example in `await expect(locator).toHaveText();`
|
||||
*/
|
||||
timeout: 5000,
|
||||
},
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: true,
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: !!process.env.CI,
|
||||
/* Retry on CI only */
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: 'html',
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
|
||||
actionTimeout: 0,
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
baseURL: process.env.CI ? 'http://remark42:8080' : 'http://127.0.0.1:8080',
|
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: 'on-first-retry',
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'firefox',
|
||||
use: {
|
||||
...devices['Desktop Firefox'],
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'webkit',
|
||||
use: {
|
||||
...devices['Desktop Safari'],
|
||||
},
|
||||
},
|
||||
|
||||
/* Test against mobile viewports. */
|
||||
// {
|
||||
// name: 'Mobile Chrome',
|
||||
// use: {
|
||||
// ...devices['Pixel 5'],
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: 'Mobile Safari',
|
||||
// use: {
|
||||
// ...devices['iPhone 12'],
|
||||
// },
|
||||
// },
|
||||
|
||||
/* Test against branded browsers. */
|
||||
// {
|
||||
// name: 'Microsoft Edge',
|
||||
// use: {
|
||||
// channel: 'msedge',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: 'Google Chrome',
|
||||
// use: {
|
||||
// channel: 'chrome',
|
||||
// },
|
||||
// },
|
||||
],
|
||||
|
||||
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
|
||||
// outputDir: 'test-results/',
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
// webServer: {
|
||||
// command: 'npm run start',
|
||||
// port: 3000,
|
||||
// },
|
||||
}
|
||||
|
||||
export default config
|
||||
@@ -1,8 +0,0 @@
|
||||
/** @type {import('prettier').Config} */
|
||||
module.exports = {
|
||||
singleQuote: true,
|
||||
semi: false,
|
||||
arrowParens: 'always',
|
||||
trailingComma: 'es5',
|
||||
printWidth: 120,
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
import { test, expect, type Page } from '@playwright/test'
|
||||
|
||||
// the parent page sets color-scheme on the iframe element from the theme param. if the iframe
|
||||
// document does not carry the same color-scheme before its bundle runs, the canvas is painted
|
||||
// opaque white instead of staying transparent. block the bundle to freeze the document in that
|
||||
// pre-script state and assert the inline head script has already applied the scheme.
|
||||
test.describe('Iframe color scheme', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.route(/remark\.m?js$/, (route) => route.abort())
|
||||
})
|
||||
|
||||
const cases = [
|
||||
{ name: 'dark theme', query: '?site_id=remark&theme=dark', expected: 'dark' },
|
||||
{ name: 'light theme', query: '?site_id=remark&theme=light', expected: 'light' },
|
||||
{ name: 'no theme falls back to light', query: '?site_id=remark', expected: 'light' },
|
||||
]
|
||||
|
||||
for (const { name, query, expected } of cases) {
|
||||
test(name, async ({ page }) => {
|
||||
await page.goto(`/web/iframe.html${query}`)
|
||||
|
||||
const inline = await page.evaluate(() => document.documentElement.style.colorScheme)
|
||||
expect(inline).toBe(expected)
|
||||
|
||||
const computed = await page.evaluate(() => getComputedStyle(document.documentElement).colorScheme)
|
||||
expect(computed).toBe(expected)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// browsers paint a default surface for an iframe before its document is parsed, and that surface
|
||||
// is opaque when the element carries a color-scheme the document does not have yet. WebKit shows
|
||||
// it as a white flash on dark host pages. the parent keeps the iframe hidden until the document
|
||||
// reports itself inited, so the surface is never presented.
|
||||
test.describe('Iframe reveal', () => {
|
||||
// REVEAL_TIMEOUT in app/utils/create-iframe.ts. the fallback timer starts when the
|
||||
// iframe is created, during page load, so any assertion with a deadline at or past
|
||||
// this value can be satisfied by the fallback alone and says nothing about the
|
||||
// message path. bound the message-path assertions well under it.
|
||||
const REVEAL_TIMEOUT = 5000
|
||||
const MESSAGE_REVEAL_BUDGET = 1500
|
||||
|
||||
const visibility = (page: Page) =>
|
||||
page.evaluate(() => {
|
||||
const iframe = document.querySelector<HTMLIFrameElement>('#remark42 iframe')
|
||||
return iframe ? iframe.style.visibility : 'no-iframe'
|
||||
})
|
||||
|
||||
test('stays hidden until the document reports inited', async ({ page }) => {
|
||||
await page.route(/\/web\/iframe\.html/, (route) => route.abort())
|
||||
const start = Date.now()
|
||||
await page.goto('/web/')
|
||||
await page.waitForSelector('#remark42 iframe', { state: 'attached' })
|
||||
|
||||
expect(await visibility(page)).toBe('hidden')
|
||||
// a slow run could have let the fallback fire, which would make the assertion
|
||||
// above pass or fail for the wrong reason. fail loudly instead of flaking.
|
||||
expect(Date.now() - start).toBeLessThan(REVEAL_TIMEOUT)
|
||||
})
|
||||
|
||||
// must reveal from the inited message, not the fallback: a broken message listener would
|
||||
// leave the widget invisible for 5s on every load. the fallback timer starts when the
|
||||
// iframe is created, partway through goto(), so bounding only the poll leaves the
|
||||
// navigation window unmeasured. time the whole thing.
|
||||
test('is revealed by the inited message, well before the fallback', async ({ page }) => {
|
||||
const start = Date.now()
|
||||
await page.goto('/web/')
|
||||
|
||||
await expect.poll(() => visibility(page), { timeout: MESSAGE_REVEAL_BUDGET }).toBe('visible')
|
||||
expect(Date.now() - start).toBeLessThan(REVEAL_TIMEOUT)
|
||||
await expect(page.locator('#remark42 iframe')).toBeVisible()
|
||||
})
|
||||
|
||||
// the aborted document never reports its height, so the iframe box stays empty and
|
||||
// toBeVisible() would fail on geometry. assert the property the fallback actually sets.
|
||||
test('is revealed by the timeout when inited never arrives', async ({ page }) => {
|
||||
await page.route(/\/web\/iframe\.html/, (route) => route.abort())
|
||||
await page.goto('/web/')
|
||||
await page.waitForSelector('#remark42 iframe', { state: 'attached' })
|
||||
|
||||
await expect.poll(() => visibility(page), { timeout: REVEAL_TIMEOUT * 2 }).toBe('visible')
|
||||
})
|
||||
})
|
||||
@@ -1,30 +0,0 @@
|
||||
import { test } from '@playwright/test'
|
||||
import { nanoid } from 'nanoid'
|
||||
import * as path from 'path'
|
||||
|
||||
test.describe('Post comment', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/web/')
|
||||
})
|
||||
|
||||
test('as dev user', async ({ page, browserName }) => {
|
||||
const iframe = page.frameLocator('iframe[name]')
|
||||
await iframe.locator('text=Sign In').click()
|
||||
const [authPage] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
iframe.locator("[title='Sign In with Dev']").click(),
|
||||
])
|
||||
await authPage.locator('text=Authorize').click()
|
||||
// triggers tab visibility and enables widget to re-render with auth state
|
||||
await page.press('iframe[name]', 'Tab')
|
||||
await iframe.locator('textarea').click()
|
||||
const message = `Hello world! ${nanoid()}`
|
||||
await iframe.locator('textarea').type(message)
|
||||
await iframe.locator('text=Send').click()
|
||||
// checks if comment was posted
|
||||
iframe.locator(`text=${message}`).first()
|
||||
await page.reload()
|
||||
// checks if saved comment is visible
|
||||
iframe.locator(`text=${message}`).first()
|
||||
})
|
||||
})
|
||||
Generated
-83
@@ -351,27 +351,6 @@ importers:
|
||||
specifier: '>=5.2.6 <6.0.0'
|
||||
version: 5.2.6(tslib@2.8.1)(webpack-cli@4.10.0)(webpack@5.108.3)
|
||||
|
||||
e2e:
|
||||
devDependencies:
|
||||
'@playwright/test':
|
||||
specifier: 1.61.1
|
||||
version: 1.61.1
|
||||
'@types/node':
|
||||
specifier: ^26.0.1
|
||||
version: 26.0.1
|
||||
nanoid:
|
||||
specifier: ^5.1.16
|
||||
version: 5.1.16
|
||||
playwright:
|
||||
specifier: 1.61.1
|
||||
version: 1.61.1
|
||||
ts-node:
|
||||
specifier: ^10.9.2
|
||||
version: 10.9.2(@swc/core@1.2.205)(@types/node@26.0.1)(typescript@5.9.3)
|
||||
typescript:
|
||||
specifier: ^5.9.3
|
||||
version: 5.9.3
|
||||
|
||||
packages:
|
||||
|
||||
'@adobe/css-tools@4.5.0':
|
||||
@@ -1542,11 +1521,6 @@ packages:
|
||||
resolution: {integrity: sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
|
||||
'@playwright/test@1.61.1':
|
||||
resolution: {integrity: sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
'@polka/url@1.0.0-next.29':
|
||||
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
|
||||
|
||||
@@ -3472,11 +3446,6 @@ packages:
|
||||
fs.realpath@1.0.0:
|
||||
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
|
||||
|
||||
fsevents@2.3.2:
|
||||
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
|
||||
fsevents@2.3.3:
|
||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
@@ -4570,11 +4539,6 @@ packages:
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
nanoid@5.1.16:
|
||||
resolution: {integrity: sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==}
|
||||
engines: {node: ^18 || >=20}
|
||||
hasBin: true
|
||||
|
||||
nanospinner@1.2.2:
|
||||
resolution: {integrity: sha512-Zt/AmG6qRU3e+WnzGGLuMCEAO/dAu45stNbHY223tUxldaDAeE+FxSPsd9Q+j+paejmm0ZbrNVs5Sraqy3dRxA==}
|
||||
|
||||
@@ -4885,16 +4849,6 @@ packages:
|
||||
resolution: {integrity: sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
|
||||
playwright-core@1.61.1:
|
||||
resolution: {integrity: sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
playwright@1.61.1:
|
||||
resolution: {integrity: sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
possible-typed-array-names@1.1.0:
|
||||
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -7938,10 +7892,6 @@ snapshots:
|
||||
tslib: 2.8.1
|
||||
tsyringe: 4.10.0
|
||||
|
||||
'@playwright/test@1.61.1':
|
||||
dependencies:
|
||||
playwright: 1.61.1
|
||||
|
||||
'@polka/url@1.0.0-next.29': {}
|
||||
|
||||
'@prefresh/babel-plugin@0.4.4': {}
|
||||
@@ -10278,9 +10228,6 @@ snapshots:
|
||||
|
||||
fs.realpath@1.0.0: {}
|
||||
|
||||
fsevents@2.3.2:
|
||||
optional: true
|
||||
|
||||
fsevents@2.3.3:
|
||||
optional: true
|
||||
|
||||
@@ -11544,8 +11491,6 @@ snapshots:
|
||||
|
||||
nanoid@3.3.18: {}
|
||||
|
||||
nanoid@5.1.16: {}
|
||||
|
||||
nanospinner@1.2.2:
|
||||
dependencies:
|
||||
picocolors: 1.1.1
|
||||
@@ -11847,14 +11792,6 @@ snapshots:
|
||||
pvutils: 1.1.5
|
||||
tslib: 2.8.1
|
||||
|
||||
playwright-core@1.61.1: {}
|
||||
|
||||
playwright@1.61.1:
|
||||
dependencies:
|
||||
playwright-core: 1.61.1
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.2
|
||||
|
||||
possible-typed-array-names@1.1.0: {}
|
||||
|
||||
postcss-attribute-case-insensitive@5.0.2(postcss@8.5.26):
|
||||
@@ -13107,26 +13044,6 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@swc/core': 1.2.205
|
||||
|
||||
ts-node@10.9.2(@swc/core@1.2.205)(@types/node@26.0.1)(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@cspotcode/source-map-support': 0.8.1
|
||||
'@tsconfig/node10': 1.0.12
|
||||
'@tsconfig/node12': 1.0.11
|
||||
'@tsconfig/node14': 1.0.3
|
||||
'@tsconfig/node16': 1.0.4
|
||||
'@types/node': 26.0.1
|
||||
acorn: 8.17.0
|
||||
acorn-walk: 8.3.5
|
||||
arg: 4.1.3
|
||||
create-require: 1.1.1
|
||||
diff: 9.0.0
|
||||
make-error: 1.3.6
|
||||
typescript: 5.9.3
|
||||
v8-compile-cache-lib: 3.0.1
|
||||
yn: 3.1.1
|
||||
optionalDependencies:
|
||||
'@swc/core': 1.2.205
|
||||
|
||||
tsconfig-paths-webpack-plugin@3.5.2:
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
packages:
|
||||
- "apps/*"
|
||||
- "e2e"
|
||||
|
||||
Reference in New Issue
Block a user