Pug support for React in two parts:
- VS Code IntelliSense inside
pug\...`` templates - Build/lint transforms for Babel, SWC, esbuild, and ESLint
For now, clone the monorepo and run the following commands from the repository root:
npm ci
npm run package:vsix
code --install-extension .tmp/vsix/vscode-react-pug-tsx.vsixOr, to build and install in one step:
npm ci
npm run install:vsixThis builds the VSIX from the monorepo into .tmp/vsix/, keeps a stable install path at .tmp/vsix/vscode-react-pug-tsx.vsix, and installs it locally into VS Code.
For embedded editor support inside style(...) blocks:
cssandscsswork with built-in VS Code supportstylrequires the VS Code extensionsysoev.language-stylussassrequires the VS Code extensionSyler.sass-indented, since built-in VS Code CSS support does not handle indented Sass syntax
TODO: publish startupjs.vscode-react-pug-tsx to the VS Code Marketplace.
After that, installation by extension id will also work:
code --install-extension startupjs.vscode-react-pug-tsxPublished package names:
@react-pug/react-pug-core@react-pug/typescript-plugin-react-pugvscode-react-pug-tsx@react-pug/babel-plugin-react-pug@react-pug/swc-plugin-react-pug@react-pug/esbuild-plugin-react-pug@react-pug/eslint-plugin-react-pug
// babel.config.js
module.exports = {
plugins: [
['@react-pug/babel-plugin-react-pug', {
tagFunction: 'pug',
sourceMaps: 'basic',
requirePugImport: false,
classShorthandProperty: 'auto',
classShorthandMerge: 'auto',
startupjsCssxjs: 'auto',
componentPathFromUppercaseClassShorthand: true
}]
]
}Babel source map modes:
sourceMaps: 'basic'(default) keeps Babel on the simple AST replacement path, replaces only matchedpugtagged-template expressions duringProgramtraversal, and produces coarse mappings for transformed Pug regions while leaving surrounding JS/TS mappings Babel-native.sourceMaps: 'detailed'enables granular mappings back into Pug content by using BabelparserOverrideplus an inline input source map. Use this when you care about devtools/debugger fidelity through later Babel transforms.
import { transformWithSwcReactPug } from '@react-pug/swc-plugin-react-pug'
const result = transformWithSwcReactPug(sourceCode, fileName, {
jsc: {
parser: { syntax: 'typescript', tsx: true },
transform: { react: { runtime: 'automatic' } }
},
sourceMaps: true
})import { build } from 'esbuild'
import { reactPugEsbuildPlugin } from '@react-pug/esbuild-plugin-react-pug'
await build({
entryPoints: ['src/index.tsx'],
bundle: true,
plugins: [reactPugEsbuildPlugin()],
sourcemap: true
})// eslint.config.js (flat config)
import reactPugPlugin from '@react-pug/eslint-plugin-react-pug'
export default [
{
files: ['**/*.{js,jsx,ts,tsx}'],
plugins: { 'react-pug': reactPugPlugin },
processor: 'react-pug/react-pug'
}
]ESLint processor behavior:
- real JS/TS diagnostics inside Pug expression sites such as
#{...},${...},tag= ..., attribute expressions, and inline handler/function bodies are mapped back to the original Pug source - formatting diagnostics for those embedded expression sites are linted against source-faithful JS wrappers rather than only against generated JSX
- autofixes and suggestions for those embedded expression sites are mapped back to the original Pug source
- diagnostics that originate only from synthetic generated helper code are filtered out
- the processor formats against the project's own
@stylisticpackage when available so generated JSX converges to the same style engine the outer ESLint run uses
Current limitation:
- multiline unbuffered
- ...statements that are authored across several Pug lines do not yet get the same source-faithful formatting-diagnostic surface as embedded expression sites - diagnostics that arise only on the generated JSX surface of a transformed Pug region still do not map autofixes back; those remain report-only unless they come from an embedded source-faithful JS site
- the internal formatter still relies on deprecated
@stylistic/jsx-indent/@stylistic/jsx-indent-propscompatibility rules, so some dependency graphs may emit a one-time deprecation warning during an ESLint run
pugReact.enabledpugReact.diagnostics.enabledpugReact.tagFunctionpugReact.requirePugImport:boolean(defaultfalse)pugReact.injectCssxjsTypes:never | auto | forcepugReact.classShorthandProperty:auto | className | class | styleNamepugReact.classShorthandMerge:auto | concatenate | classnamespugReact.componentPathFromUppercaseClassShorthand:boolean(defaulttrue)
Import handling:
- used
pugimports are removed automatically from transformed output and shadow documents - when
requirePugImportis enabled, using the configured tag without an explicit import is treated as an error - when a terminal
styleblock is used,pugmust be imported so the matchingcss/styl/sass/scsshelper can be resolved from the same module
Class shorthand behavior:
- default auto:
className+ string concatenation - auto with
startupjs/cssxjsmarker:styleName+ classnames-style array merge
npm ci
npm run typecheck
npm run build
npm run test:core
npm run test:ts-plugin
npm run test:vscode
npm testIf you are working in an environment with NODE_ENV=production, install dev dependencies explicitly:
npm ci --include=devUseful:
npm run test:vscode:example:screenshots
npm run vscode:fresh:example
npm run check:pug:example
npx @react-pug/check-types .
npx @react-pug/check-types src/App.tsx src/Button.tsxPug-aware CI type check for a target project (without VS Code UI):
npx @react-pug/check-types <project-dir>If you are using StartupJS, the same checker can be exposed as:
npx startupjs check@react-pug/react-pug-corefinds tagged templates and compiles Pug regions.- For editor tooling, the TS plugin builds a shadow document and remaps LS results.
- For build/lint tooling, compiler adapters transform source and remap diagnostics to original Pug ranges.
Terminal style blocks can be embedded at the end of a pug template:
pug`
.title Hello
style(lang='styl')
.title
color red
`Behavior:
styledefaults tocss- supported langs:
css,styl,sass,scss - the
styleblock must be the last top-level node in the template - its content is moved to the top of the immediate enclosing scope as
css```,styl, `sassorscss``` and keeps${...}` interpolations intact - target scope selection:
- nearest enclosing block scope
- expression-bodied arrow functions are rewritten so the helper call can be inserted before the returned expression
- single-line
if/else/ loop statement bodies are normalized into blocks when needed so the helper call can be inserted before the original statement Programscope inserts right after the last import or directive
- the helper import is added from the same module as the file's
pugimport unless it already exists
- tags/components
- attrs and spread attrs
- class/id shorthand
- interpolation:
#{},!{}, and${}(including nestedpug) - line expressions:
tag= expression - control flow:
if,else if,else,each,while,case/when - unbuffered code:
- ... - text nodes and
|piped text - terminal
styleblocks with embedded CSS/SCSS editor support, plus Stylus/Sass when the corresponding VS Code language extension is installed
- VS Code extension currently targets desktop extension host (not web extension host).
- During heavily malformed in-progress edits, temporary mapping can be approximate until syntax stabilizes.
- Babel
sourceMaps: 'basic'is compatibility-first and does not preserve fine-grained mappings within a transformed Pug region. Surrounding non-Pug JS/TS code keeps normal Babel mappings. UsesourceMaps: 'detailed'when you need granular Babel source maps inside Pug. - Embedded Stylus editor IntelliSense depends on the external VS Code Stylus extension being installed.
- Embedded Sass editor IntelliSense/highlighting depends on the VS Code extension
Syler.sass-indentedbeing installed. Built-in VS Code CSS support handlescssandscss, but not indentedsass.
If you are working on the repo itself and are cold-starting on the implementation, read these first:
- architecture.md: current package boundaries, transform/mapping model, ESLint/TS/VS Code architecture
- plan.md: current status, known limitations, validation expectations, and recommended next work
- performance.md: where cost actually lives and what is or is not worth optimizing
This is mainly for contributors and agents doing actual improvements to the repo, not for normal users of the packages. Those docs capture the current architecture, the known contract boundaries, and the operational gotchas that are easy to miss if you jump straight into the code.