Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ inputs:
node-version:
description: The version of Node.js to install
required: true
default: 20.16.0
default: 20.19.0

runs:
using: composite
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ jobs:
- name: Run Next.js E2E tests
run: pnpm --filter frontend-nextjs test:e2e
- name: Upload test results
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
if: always()
with:
name: nextjs-e2e-results
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/checking-dependencies.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: pnpm/action-setup@v5
- uses: actions/setup-node@v6
with:
node-version: 24.13.1
node-version: 24.14.1
cache: pnpm

- run: pnpm install --frozen-lockfile
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/snapshot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ jobs:
npm pack --silent --pack-destination ../../artifacts
- name: Upload snapshot artifacts
if: steps.snapshot.outputs.success != 'true'
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: context-doc-snapshot
path: artifacts/*.tgz
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "1.0.0",
"private": true,
"description": "Monorepo workspace for effect-template",
"packageManager": "pnpm@10.30.1",
"packageManager": "pnpm@10.33.0",
"workspaces": [
"packages/*"
],
Expand All @@ -23,8 +23,8 @@
"start": "pnpm --filter @prover-coder-ai/component-tagger start"
},
"devDependencies": {
"@changesets/changelog-github": "^0.5.2",
"@changesets/cli": "^2.29.8"
"@changesets/changelog-github": "^0.6.0",
"@changesets/cli": "^2.30.0"
},
"repository": {
"type": "git",
Expand Down
42 changes: 21 additions & 21 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,45 +55,45 @@
"url": "https://github.com/ProverCoderAI/effect-template/issues"
},
"homepage": "https://github.com/ProverCoderAI/effect-template#readme",
"packageManager": "pnpm@10.30.1",
"packageManager": "pnpm@10.33.0",
"dependencies": {
"@babel/core": "^7.29.0",
"@effect/platform": "^0.94.5",
"@effect/platform-node": "^0.104.1",
"effect": "^3.19.18"
"@effect/platform": "^0.96.0",
"@effect/platform-node": "^0.106.0",
"effect": "^3.21.0"
},
"peerDependencies": {
"vite": "^7.3.1"
"vite": "^8.0.3"
},
"devDependencies": {
"@types/babel__core": "^7.20.5",
"@biomejs/biome": "^2.4.4",
"@biomejs/biome": "^2.4.9",
"@effect/eslint-plugin": "^0.3.2",
"@effect/language-service": "latest",
"@effect/vitest": "^0.27.0",
"@eslint-community/eslint-plugin-eslint-comments": "^4.6.0",
"@eslint/compat": "2.0.2",
"@eslint/eslintrc": "3.3.3",
"@effect/vitest": "^0.29.0",
"@eslint-community/eslint-plugin-eslint-comments": "^4.7.1",
"@eslint/compat": "2.0.3",
"@eslint/eslintrc": "3.3.5",
"@eslint/js": "10.0.1",
"@prover-coder-ai/eslint-plugin-suggest-members": "^0.0.25",
"@ton-ai-core/vibecode-linter": "^1.0.11",
"@types/node": "^24.10.13",
"@typescript-eslint/eslint-plugin": "^8.56.0",
"@typescript-eslint/parser": "^8.56.0",
"typescript-eslint": "^8.56.0",
"@vitest/coverage-v8": "^4.0.18",
"eslint": "^10.0.1",
"@types/node": "^24.12.0",
"@typescript-eslint/eslint-plugin": "^8.57.2",
"@typescript-eslint/parser": "^8.57.2",
"typescript-eslint": "^8.57.2",
"@vitest/coverage-v8": "^4.1.2",
"eslint": "^10.1.0",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-codegen": "0.34.1",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-simple-import-sort": "^12.1.1",
"eslint-plugin-sonarjs": "^4.0.0",
"eslint-plugin-sonarjs": "^4.0.2",
"eslint-plugin-sort-destructure-keys": "^3.0.0",
"eslint-plugin-unicorn": "^63.0.0",
"@vitest/eslint-plugin": "^1.6.9",
"globals": "^17.3.0",
"@vitest/eslint-plugin": "^1.6.13",
"globals": "^17.4.0",
"jscpd": "^4.0.8",
"typescript": "^5.9.3",
"vitest": "^4.0.18"
"typescript": "^6.0.2",
"vitest": "^4.1.2"
}
}
53 changes: 38 additions & 15 deletions packages/app/src/shell/component-tagger.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type PluginObj, transformAsync, types as t } from "@babel/core"
import type { Path } from "@effect/platform/Path"
import { Effect, pipe } from "effect"
import type { PluginOption } from "vite"
import type { Plugin } from "vite"

import { babelPluginName, componentPathAttributeName, isJsxFile, normalizeModuleId } from "../core/component-path.js"
import { createJsxTaggerVisitor, type JsxTaggerContext } from "../core/jsx-tagger.js"
Expand All @@ -20,9 +20,11 @@ export type ComponentTaggerOptions = {

type BabelTransformResult = Awaited<ReturnType<typeof transformAsync>>

type BabelSourceMap = NonNullable<NonNullable<BabelTransformResult>["map"]>

type ViteTransformResult = {
readonly code: string
readonly map: NonNullable<BabelTransformResult>["map"] | null
readonly map: string | null
}

class ComponentTaggerError extends Error {
Expand All @@ -33,16 +35,35 @@ class ComponentTaggerError extends Error {
}
}

const toViteResult = (result: BabelTransformResult): ViteTransformResult | null => {
if (result === null || result.code === null || result.code === undefined) {
const toSourceMapInput = (map: BabelSourceMap | null | undefined): string | null => {
if (map === null || map === undefined) {
return null
}

return JSON.stringify({
file: map.file,
mappings: map.mappings,
names: map.names,
sources: map.sources,
version: map.version,
...(map.sourceRoot === undefined ? {} : { sourceRoot: map.sourceRoot }),
...(map.sourcesContent === undefined ? {} : { sourcesContent: map.sourcesContent })
})
}

const toViteResult = (result: BabelTransformResult, originalCode: string): ViteTransformResult => {
if (result === null || result.code === null || result.code === undefined) {
return {
code: originalCode,
map: null
}
}

const { code } = result

return {
code,
map: result.map ?? null
map: toSourceMapInput(result.map)
}
}

Expand Down Expand Up @@ -73,7 +94,7 @@ const makeBabelTagger = (relativeFilename: string, attributeName: string): Plugi
*
* @param code - Source code to transform.
* @param id - Vite module id for the source code.
* @returns Vite-compatible transform result or null when no output is produced.
* @returns Vite-compatible transform result.
*
* @pure false
* @effect Babel transform
Expand All @@ -85,17 +106,17 @@ const makeBabelTagger = (relativeFilename: string, attributeName: string): Plugi
// QUOTE(TZ): "Сам компонент должен быть в текущем app но вот что бы его протестировать надо создать ещё один проект который наш текущий апп будет подключать"
// REF: user-2026-01-14-frontend-consumer
// SOURCE: n/a
// FORMAT THEOREM: forall c in Code: transform(c) = r -> r is tagged or null
// FORMAT THEOREM: forall c in Code: transform(c) = r -> r is tagged
// PURITY: SHELL
// EFFECT: Effect<ViteTransformResult | null, ComponentTaggerError, never>
// EFFECT: Effect<ViteTransformResult, ComponentTaggerError, never>
// INVARIANT: errors are surfaced as ComponentTaggerError only
// COMPLEXITY: O(n)/O(1)
const runTransform = (
code: string,
id: string,
rootDir: string,
attributeName: string
): Effect.Effect<ViteTransformResult | null, ComponentTaggerError, Path> => {
): Effect.Effect<ViteTransformResult, ComponentTaggerError, Path> => {
const cleanId = normalizeModuleId(id)

return pipe(
Expand All @@ -120,15 +141,15 @@ const runTransform = (
}
})
),
Effect.map((result) => toViteResult(result))
Effect.map((result) => toViteResult(result, code))
)
}

/**
* Creates a Vite plugin that injects a single component-path data attribute.
*
* @param options - Configuration options for the plugin.
* @returns Vite PluginOption for pre-transform tagging.
* @returns Vite plugin for pre-transform tagging.
*
* @pure false
* @effect Babel transform through Effect
Expand All @@ -143,26 +164,28 @@ const runTransform = (
// SOURCE: n/a
// FORMAT THEOREM: forall id: isJsxFile(id) -> transform(id) adds specified attribute
// PURITY: SHELL
// EFFECT: Effect<ViteTransformResult | null, ComponentTaggerError, never>
// EFFECT: Effect<ViteTransformResult, ComponentTaggerError, never>
// INVARIANT: no duplicate attributes with the same name
// COMPLEXITY: O(n)/O(1)
export const componentTagger = (options?: ComponentTaggerOptions): PluginOption => {
export const componentTagger = (options?: ComponentTaggerOptions): Plugin => {
const attributeName = options?.attributeName ?? componentPathAttributeName
let resolvedRoot = process.cwd()

return {
const plugin: Plugin = {
name: "component-path-tagger",
enforce: "pre",
apply: "serve",
configResolved(config) {
resolvedRoot = config.root
},
transform(code, id) {
transform(code, id, _options) {
if (!isJsxFile(id)) {
return null
}

return Effect.runPromise(pipe(runTransform(code, id, resolvedRoot, attributeName), Effect.provide(NodePathLayer)))
}
}

return plugin
}
11 changes: 5 additions & 6 deletions packages/app/tests/shell/component-tagger.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { describe, expect, it } from "@effect/vitest"
import { Effect } from "effect"
import type { Plugin } from "vite"

import { componentTagger } from "../../src/shell/component-tagger.js"

Expand All @@ -19,36 +18,36 @@ describe("component-tagger (Vite plugin)", () => {
describe("componentTagger", () => {
it.effect("returns a Vite plugin with correct name", () =>
Effect.sync(() => {
const plugin = componentTagger() as Plugin
const plugin = componentTagger()

expect(plugin).toBeDefined()
expect(plugin.name).toBe("component-path-tagger")
}))

it.effect("has enforce: pre configuration", () =>
Effect.sync(() => {
const plugin = componentTagger() as Plugin
const plugin = componentTagger()

expect(plugin.enforce).toBe("pre")
}))

it.effect("applies only in serve mode", () =>
Effect.sync(() => {
const plugin = componentTagger() as Plugin
const plugin = componentTagger()

expect(plugin.apply).toBe("serve")
}))

it.effect("has transform hook", () =>
Effect.sync(() => {
const plugin = componentTagger() as Plugin
const plugin = componentTagger()

expect(plugin.transform).toBeDefined()
}))

it.effect("has configResolved hook", () =>
Effect.sync(() => {
const plugin = componentTagger() as Plugin
const plugin = componentTagger()

expect(plugin.configResolved).toBeDefined()
}))
Expand Down
1 change: 1 addition & 0 deletions packages/app/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"compilerOptions": {
"rootDir": ".",
"outDir": "dist",
"ignoreDeprecations": "6.0",
"types": ["vitest"],
"baseUrl": ".",
"paths": {
Expand Down
6 changes: 3 additions & 3 deletions packages/frontend-nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@
},
"dependencies": {
"@prover-coder-ai/component-tagger": "workspace:*",
"next": "^16.1.6",
"next": "^16.2.1",
"react": "^19.2.4",
"react-dom": "^19.2.4"
},
"devDependencies": {
"@babel/core": "^7.29.0",
"@playwright/test": "^1.58.2",
"@types/node": "^24.10.13",
"@types/node": "^24.12.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"typescript": "^5.9.3"
"typescript": "^6.0.2"
}
}
8 changes: 4 additions & 4 deletions packages/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
},
"devDependencies": {
"@playwright/test": "^1.58.2",
"@types/node": "^24.10.13",
"@types/node": "^24.12.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.4",
"typescript": "^5.9.3",
"vite": "^7.3.1"
"@vitejs/plugin-react": "^6.0.1",
"typescript": "^6.0.2",
"vite": "^8.0.3"
}
}
4 changes: 2 additions & 2 deletions packages/pack-consumer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"@prover-coder-ai/component-tagger": "file:.pack/component-tagger.tgz"
},
"devDependencies": {
"typescript": "^5.9.3",
"vite": "^7.3.1"
"typescript": "^6.0.2",
"vite": "^8.0.3"
}
}
Loading
Loading