fix(create-vc-app): make scaffolded apps build and lint out of the box - #302
Open
maksimzinchuk wants to merge 2 commits into
Open
fix(create-vc-app): make scaffolded apps build and lint out of the box#302maksimzinchuk wants to merge 2 commits into
maksimzinchuk wants to merge 2 commits into
Conversation
|
📦 Preview published for commit Install the preview with dist-tag: npm install @vc-shell/framework@pr-302Or pin to the exact commit: npm install @vc-shell/framework@2.4.0-pr302.45ba88bPublished packages (dist-tag
|
`tw-text-s` is not in the framework's fontSize scale (xxs/xs/sm/base/...), so `@apply` failed at the PostCSS stage and `yarn build` errored in every generated app. `Welcome.vue` and `routes.ts.ejs` also carried `eslint-disable` comments for `import/no-unresolved` — a rule no config registered — which made `yarn lint` fail with "Definition for rule 'import/no-unresolved' was not found". Templates are `.ejs`, so ESLint and Prettier structurally cannot check them, and the templates directory was additionally listed in `.prettierignore` and the root eslint `ignores`. Nothing verified this code. Both ignores are now narrowed to `**/_yarn`, and the generated output is gated by tests instead. - fix the class name and drop the unused `bgImage` import - wire eslint-plugin-import and the typescript resolver in the app template and in the monorepo root; disable the rules that parse dependency sources with espree, which chokes on modern syntax in `dist` output - slurp EJS whitespace in `routes.ts.ejs`: bare `<% %>` control tags emitted blank lines that a Prettier pass cannot remove, since Prettier preserves a single blank line between statements - run Prettier over the generated tree from `init`, and over just the touched paths from `add-module`, so the regex splicing in `codegen.ts` is normalised. `resolveConfig` is called with `editorconfig: true`, otherwise the API ignores `.editorconfig` and rewraps at 80 columns instead of the project's 120 - add 8 tests: generated projects must be Prettier-clean across four flag combinations, and the raw `renderDir` output must be free of EJS whitespace artifacts. The second layer is required because the formatter would otherwise mask template bugs - collapse duplicate imports the newly enabled `import/no-duplicates` reports Scaffolding a project and running `yarn install && yarn build && yarn type-check && yarn lint` now passes, and the app boots to the login page. Restoring `tw-text-s` fails the build again.
maksimzinchuk
force-pushed
the
fix/scaffolding-lint-and-format
branch
from
August 17, 2026 14:09
e9fab59 to
99e48e1
Compare
…ing edits
`codegen.ts` edited existing source with regexes and had no test coverage. Three
defects, each reproduced before fixing:
- `add-module` never registered the module. The anchor pattern was
`/\.use\([A-Z]\w+,\s*\{\s*router\s*\}\)/`, but the template emits
`app.use(Orders);`. Nothing matched, `lastMatch` stayed null, no `app.use()`
was inserted — and the command still printed
"✔ Updated src/main.ts — added import & app.use(Reviews)". The module was
imported, unused, and never registered, so it simply did not work.
- `insertImport` matched `/^import\s.+$/gm`, which sees only the first line of a
multi-line import. When the last import spanned lines, the new statement was
spliced between `import {` and its first specifier.
- `addMenuItemToBootstrap` matched `/addMenuItem\(\{[\s\S]*?\}\);/` lazily, so a
menu item containing a literal `});` (any nested callback) truncated the match
and the new item landed inside the previous one's body.
Structural edits now run against a mask of the source in which comment and
string contents are replaced by spaces, offsets preserved. Call boundaries come
from bracket matching rather than a lazy quantifier, nested calls are skipped,
import statements are scanned to their terminating `;`, and insertions are
applied highest-offset-first so earlier positions stay valid.
`addModuleToMain` now throws when an anchor is missing instead of reporting a
change it did not make; `add-module` already catches that and prints what to add
by hand, and the file is left untouched.
Adds `codegen.test.ts` (9 cases, the file had none). Each result is run through
Prettier, which throws on a syntax error — that alone would have caught two of
the three defects.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Three defects reported against the scaffolding, all confirmed:
A generated app did not build.
Welcome.vueusedtw-text-s, which is not in the framework'sfontSizescale (xxs / xs / sm / base / lg / …).@applywith a non-existent class is a hard PostCSS error, not a silent no-op:yarn lintfailed out of the box.eslint-plugin-importandeslint-import-resolver-typescriptwere declared in the template'sdevDependenciesbut never wired intoeslint.config.mjs, whileWelcome.vueandroutes.ts.ejscarriedeslint-disablecomments forimport/no-unresolved. ESLint treats a disable directive for an unregistered rule as an error:Template code was never linted or formatted.
cli/create-vc-app/src/templateswas listed in both.prettierignoreand the root eslintignores, so even the plain.ts/.vuefiles there were unchecked.prettier --checkflagged 8 files, including atsconfig.jsonwith broken indentation.The root cause behind all three: templates are
.ejs, so ESLint and Prettier structurally cannot check them. Nothing verified this code, and the extra ignore entries hid even the files that were checkable.Approach
Templates cannot be linted directly, so the gate has to be generate a project and check the output. That needs two independent layers — neither alone is sufficient:
<%_ … -%>). A bare<% if %>on its own line emits a blank line into the output.bootstrap.ts.ejsandmain.ts.ejsalready did this correctly;routes.ts.ejsdid not, producing output riddled with stray blank lines.src/engine/format.ts), called frominit(whole tree) andadd-module(only the touched paths, so it never rewrites files the user owns). This is what normalises the regex splicing incodegen.ts.Prettier alone cannot replace layer 1: it collapses 2+ blank lines to one but preserves a single blank line between statements, so EJS artifacts survive a format pass. Layer 1 alone cannot replace layer 2 either, since it does nothing about the string splicing in
codegen.ts.Changes
tw-text-s→tw-text-sm; drop the unusedbgImageimport.eslint-plugin-import+ the typescript resolver in the app template and in the monorepo root.flatConfigs.recommendedmust be spread before the Vue/TS configs (it sets a globallanguageOptions.ecmaVersionwith nofilesscope and would otherwise override their parsers), and the resolver setting must come afterflatConfigs.typescript(which presets anoderesolver).import/namespace,import/default,import/no-named-as-default{,-member}. These parse the dependency's source with espree and produce 48 bogusParse errors in imported module '@vc-shell/framework'on a real app. TypeScript already covers them;flatConfigs.typescriptonly disablesimport/named..prettierignoreand root eslintignoresentries to**/_yarn, so the plain template files are covered byyarn formatandyarn lintagain; format the 8 files this surfaced.renderDiroutput is free of EJS whitespace artifacts. The second group is required — without it the formatter would silently absorb template bugs.import/no-duplicatesreports (10 framework files, plus three separatefrom "vue"statements inPullToRefresh.vuethat--fixcould not merge).framework/index.tsexports ~18 names both explicitly and viaexport *from the same module. That is legal — an explicit export shadows a star export — butimport/exportcannot tell it apart from a real collision, so the rule is disabled for that one file. Cleaning up the barrel is left as a follow-up.Second commit:
add-modulecodegenWriting the tests above surfaced that
codegen.ts— which edits existing source with regexes — had no test coverage and three defects. Each was reproduced before being fixed.add-modulenever registered the module. The anchor was/\.use\([A-Z]\w+,\s*\{\s*router\s*\}\)/, but the template emitsapp.use(Orders);. Nothing matched,lastMatchstayednull, noapp.use()was inserted — and the command still printed:The module was imported, unused, and never registered, so the feature silently did nothing. This is current behaviour on
main, not a hypothetical.A multi-line trailing import got corrupted.
insertImportmatched/^import\s.+$/gm, which sees only the first line of a multi-line import:A nested
});truncated the menu-item match./addMenuItem\(\{[\s\S]*?\}\);/is lazy, so any menu item containing a callback that ends in});caused the new item to land inside the previous one's body.Structural edits now run against a mask of the source with comment and string contents replaced by spaces and offsets preserved, so a brace or semicolon inside a string cannot be read as syntax. Call boundaries come from bracket matching instead of a lazy quantifier, nested calls are skipped, imports are scanned to their terminating
;, and insertions apply highest-offset-first so earlier positions stay valid.addModuleToMainthrows when an anchor is missing rather than reporting a change it did not make —add-modulealready catches that and prints manual instructions, and the file is left untouched.codegen.test.tsadds 9 cases. Each result is run through Prettier, which throws on a syntax error; that check alone would have caught two of the three defects.magicastis declared incli/create-vc-app/dependenciesand never imported anywhere — a dead dependency, and ironically a library built for exactly this kind of edit. Left alone here; removing it or migratingcodegen.tsonto it is a separate call.Verification
Scaffolded an app with the built CLI (
standalone, module, dashboard, tenant routes, mocks) plusadd-module, then ran it for real:yarn installyarn build(vite build+vue-tsc)dist/index.html+dist/types/producedyarn type-checkyarn lintyarn preview→ HTTP200, correct title, CSS and JS bundles served#/login, login form renders, nopageerrorAfter
add-module reviews,main.tsnow reads:and the app with the registered module still builds, type-checks and lints clean.
The emitted CSS is correct, not merely non-crashing:
.875remis exactly thesmvalue fromframework/tailwind.config.ts.Negative controls — each fix was checked to be load-bearing:
tw-text-sin the built app failsvite buildwith the PostCSS error above.formatGeneratedfails only the 4 Prettier tests.All CI gates were run in a fresh, unbuilt worktree (matching what CI sees):
lint:check,format:check,stylelint:check,typecheck,check:locales,check:circular,check:layers— all exit 0. Framework suite: 3791 tests in 403 files pass. CLI suite: 67 tests (was 50).Three things made a local green run misleading, which is worth knowing for anyone touching these configs:
yarn install --immutable→yarn lint:check, no build step in between). Every@vc-shell/*workspace package resolves throughmain/typesfields pointing intodist/, so none of them resolve on CI. A leftoverframework/disthides this locally.src/is platform-dependent. With the ignore narrowed to^@vc-shell/framework/, the Linux runner reported 6import/no-unresolvederrors for@vc-shell/mf-configand@vc-shell/config-generatorthat macOS did not — reproduced neither by deletingframework/distnor by a cleanyarn install --immutable.eslint-import-resolver-typescriptv4 uses the nativeunrs-resolver, which behaves differently per platform. Hence the ignore covers^@vc-shell/rather than relying on that fallback.yarn lint≠yarn lint:check. The former runs--fixand ignores warnings; CI runs the latter with--max-warnings=0. A greenyarn lintsays nothing about CI.Notes
A freshly generated app still reports 8
@typescript-eslint/no-unused-varswarnings (yarn lintexits 0). These are intentional stubs: the parameters sit next to commented-out TODO API calls that reference them, so renaming to_querywould break the code the user is meant to uncomment. Left as-is deliberately.