Skip to content

Commit 5c9d6cf

Browse files
CopilotHerrCai0907Cai Congcong
authored
initialize printer synchronously at module load (#16)
The plugin exports an empty `as_estree` printer object that gets populated asynchronously during `parse()`. Prettier attempts to use the printer before initialization completes, causing `TypeError: printer.print is not a function` on Prettier 3.x. ## Changes - **Import estree plugin explicitly** at module load to access the base printer - **Initialize `as_estree` synchronously** by spreading `pluginEstree.printers.estree` at module level - **Remove async initialization path** - deleted `initPrinter()` function and made `parse()` synchronous - **Add test** verifying printer methods are available at module load time ## Before/After ```javascript // Before: printer empty at export, populated during first parse let as_estree = {}; async function initPrinter(jsPlugin) { let estree = jsPlugin.printers.estree; estree = typeof estree == "function" ? await estree() : estree; Object.assign(as_estree, { ...estree, printComment(...) {...} }); } async function parse(text, options) { await initPrinter(options.plugins.find(...)); // Race condition return pluginTypescript.parsers.typescript.parse(text, options); } // After: printer fully initialized at module load import pluginEstree from "prettier/plugins/estree"; const baseEstreePrinter = pluginEstree.printers.estree; const as_estree = { ...baseEstreePrinter, printComment(...) {...} }; function parse(text, options) { return pluginTypescript.parsers.typescript.parse(text, options); } ``` The printer now has all required methods (`print`, `printComment`, etc.) immediately available when the plugin is loaded. <!-- START COPILOT ORIGINAL PROMPT --> <details> <summary>Original prompt</summary> ---- *This section details on the original issue you should resolve* <issue_title>BUG: `TypeError: printer.print is not a function` with Prettier 3.x</issue_title> <issue_description>## Summary The `assemblyscript-prettier` plugin fails with `TypeError: printer.print is not a function` when used with Prettier 3.x. This affects both version 3.0.1 (latest) and version 2.0.2. ## Environment - **Node.js:** v20.x / v22.x - **Prettier:** 3.3.3+ - **assemblyscript-prettier:** Tested with 3.0.1 and 2.0.2 - **OS:** Linux (Debian 13 / Ubuntu) ## Steps to Reproduce ### 1. Install the plugin ```bash npm i -D assemblyscript-prettier@3.0.1 # or @2.0.2 ``` ### 2. Configure Prettier (any of these approaches) **Option A - Root `.prettierrc.json`:** ```json { "semi": false, "singleQuote": true, "trailingComma": "none", "plugins": ["assemblyscript-prettier"] } ``` **Option B - Directory-scoped `.prettierrc.json`:** Place the config in the AssemblyScript directory (e.g., `assembly/.prettierrc.json`) ### 3. Run Prettier ```bash npx prettier --write . ``` ## Expected Behavior AssemblyScript files with decorators like `@inline`, `@lazy`, `@external` should be formatted correctly. ## Actual Behavior ``` [error] assembly/index.ts: TypeError: printer.print is not a function [error] at callPluginPrintFunction (file:///node_modules/prettier/index.mjs:16631:20) [error] at printAstToDoc (file:///node_modules/prettier/index.mjs:16581:22) [error] at async coreFormat (file:///node_modules/prettier/index.mjs:16959:14) [error] at async formatWithCursor (file:///node_modules/prettier/index.mjs:17172:14) [error] at async formatFiles (file:///node_modules/prettier/internal/legacy-cli.mjs:5831:18) ``` The error occurs on **all** `.ts` files processed by the plugin, not just AssemblyScript files. ## Root Cause Analysis Looking at the plugin source code in `src/plugin.js`: ### Version 3.0.1 Issue ```javascript let as_estree = {}; async function initPrinter(jsPlugin) { let estree = jsPlugin.printers.estree; estree = typeof estree == "function" ? await estree() : estree; Object.assign(as_estree, { ...estree, // ... }); } async function parse(text, options) { await initPrinter(options.plugins.find((plugin) => plugin.printers && plugin.printers.estree)); // ... } export default { parsers: { typescript: { ...pluginTypescript.parsers.typescript, parse, astFormat: "as-estree", preprocess: preProcess, }, }, printers: { "as-estree": as_estree }, // Empty object at registration time! }; ``` ### Version 2.0.2 Issue ```javascript let as_estree = {}; function initPrinter(jsPlugin) { Object.assign(as_estree, { ...jsPlugin.printers.estree, // ... }); } function parse(text, options) { initPrinter(options.plugins.find((plugin) => plugin.printers && plugin.printers.estree)); // ... } export default { parsers: { typescript: { /* ... */ }, }, printers: { "as-estree": as_estree }, // Still empty at registration time! }; ``` **The problem:** The `as_estree` printer is registered as an empty object (`{}`) at module load time. The `initPrinter` function that populates it is called during `parse()`, but Prettier attempts to use the printer **before** the first parse completes. When Prettier calls `printer.print()`, the `as_estree` object is still empty because: 1. Plugin exports are evaluated at module load 2. `as_estree = {}` is empty at that point 3. `initPrinter()` hasn't been called yet 4. Prettier tries to use the printer before parsing the first file ## Additional Issue: Parser Scope The plugin overrides the built-in `typescript` parser globally: ```javascript export default { parsers: { typescript: { /* ... */ }, // Replaces Prettier's typescript parser! }, // ... }; ``` This means when the plugin is active, **all** `.ts` files are processed with the AssemblyScript parser, not just files in `assembly/` directories. This is problematic for monorepos or projects with both regular TypeScript and AssemblyScript. ## Suggested Fix The printer should be initialized synchronously at module load time, not lazily during parse: ```javascript import pluginTypescript from "prettier/plugins/typescript"; import { magic, preProcess } from "./replace.js"; import { builders } from "prettier/doc"; // Get the estree printer synchronously at module load const tsPlugin = pluginTypescript; const as_estree = { // Copy all methods from the TypeScript estree printer ...(() => { const estree = tsPlugin.printers?.estree; return typeof estree === "function" ? null : estree; // Handle lazy loading differently })(), printComment(commentPath, options) { const comment = commentPath.getValue().value; if (comment.startsWith(magic) && comment.endsWith(magic)) { const doc = []; if (commentPath.stack[commentPath.stack.length - 2] === 0) { doc.push(builders.hardline); } doc.pus... </details> <!-- START COPILOT CODING AGENT SUFFIX --> - Fixes #15 <!-- START COPILOT CODING AGENT TIPS --> --- ✨ Let Copilot coding agent [set things up for you](https://github.com/wasm-ecosystem/assemblyscript-prettier/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: HerrCai0907 <77575210+HerrCai0907@users.noreply.github.com> Co-authored-by: Cai Congcong <congcong.cai@bmw.com>
1 parent a5e9b1e commit 5c9d6cf

5 files changed

Lines changed: 1078 additions & 3675 deletions

File tree

.github/workflows/nodejs.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@ jobs:
1111
runs-on: ubuntu-latest
1212
steps:
1313
- uses: actions/checkout@v3
14-
- name: Use Node.js 16.x
15-
uses: actions/setup-node@v3
14+
- uses: actions/setup-node@v3
1615
with:
17-
node-version: 16.x
16+
node-version: 22.x
1817
cache: "npm"
1918
- run: npm ci
2019
- run: npm run lint

.github/workflows/publish.yml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,8 @@ jobs:
1212
- uses: actions/checkout@v2
1313
- uses: actions/setup-node@v2
1414
with:
15-
node-version: "18.x"
15+
node-version: "22.x"
1616
- name: Install dependencies
1717
run: npm ci
1818

19-
- run: npm version $(echo "${GITHUB_REF}" | sed 's/refs\/tags\///') --git-tag-version false
20-
- run: echo "//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}" >> .npmrc
21-
env:
22-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
2319
- run: npm publish

0 commit comments

Comments
 (0)