diff --git a/.prettierrc.json b/.prettierrc.json index b1e6b2baf..095bf4e98 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -2,5 +2,11 @@ "singleQuote": true, "tabWidth": 2, "semi": true, - "endOfLine": "lf" + "endOfLine": "lf", + "overrides": [ + { + "files": "*.vue", + "options": { "htmlWhitespaceSensitivity": "strict" } + } + ] } diff --git a/build/vueLoader.js b/build/vueLoader.js index f7a552945..a549fca47 100644 --- a/build/vueLoader.js +++ b/build/vueLoader.js @@ -9,7 +9,11 @@ import { VUE_TEMPLATE_OPTIONS } from './vueOptions.js'; * Styles are dropped: tests read the global stylesheet separately. */ export function compileVueModule(source, filename) { - const { descriptor, errors } = parse(source, { filename }); + // The template is parsed with the SFC; parse it as the browser build does. + const { descriptor, errors } = parse(source, { + filename, + templateParseOptions: VUE_TEMPLATE_OPTIONS.compilerOptions, + }); if (errors.length) throw errors[0]; const id = filename; if (descriptor.scriptSetup) { diff --git a/build/vueOptions.js b/build/vueOptions.js index dcb92419c..bd8be66fd 100644 --- a/build/vueOptions.js +++ b/build/vueOptions.js @@ -1,16 +1,12 @@ /** - * Template compiler options shared by the browser build, the boundary check - * and the unit-test loader. Whitespace between nodes is kept so a view renders - * as its static markup did: inline layouts depend on text between nodes. - */ -const VUE_COMPILER_OPTIONS = Object.freeze({ whitespace: 'preserve' }); - -/** - * SFC template options. Asset URLs such as `/pin.svg` stay literal, as they - * were in the static markup, instead of becoming module imports. + * Template options shared by the browser build, the boundary check and the + * unit-test loader. Whitespace between nodes is kept (as a single space) so + * an application view renders as its static markup did; Prettier formats + * single-file components with strict whitespace sensitivity to match. Asset + * URLs such as `/pin.svg` stay literal instead of becoming module imports. */ export const VUE_TEMPLATE_OPTIONS = Object.freeze({ - compilerOptions: VUE_COMPILER_OPTIONS, + compilerOptions: Object.freeze({ whitespace: 'preserve' }), transformAssetUrls: false, }); diff --git a/docs/VUE-MIGRATION.md b/docs/VUE-MIGRATION.md index 370fc3240..52067f981 100644 --- a/docs/VUE-MIGRATION.md +++ b/docs/VUE-MIGRATION.md @@ -61,11 +61,23 @@ They contain no application state. need no change. Views belong to the page and are never unmounted. The loading screen stays static so it paints before any script runs. Markup tests read `renderApplicationHtml()` from `src/testSupport`, which renders - the views on the server. Vue keeps the whitespace between elements as a - single space, so a view renders as its template did; no view markup sits - under a `white-space: pre` rule. The markup has moved, unchanged: every - template except the loading screen is a view. Next each view takes over - its wiring. Order: Scenes, Weather, Recent imagery, + the views on the server. Templates compile with Vue's `preserve` + whitespace, which keeps the whitespace between elements as a single + space, so a view renders as its template did; no view markup sits under a + `white-space: pre` rule. Application views (`src/ui/views/*View.vue`) are + kept out of Prettier; other components are formatted with strict + whitespace sensitivity, so their source spacing is what renders. The + markup has moved, unchanged: + every template except the loading screen is a view. + + Next each view takes over its wiring: markup an owner builds with + `createElement` becomes component markup that renders from a Pinia store + the owner publishes to, and user actions come back to the owner through + the store, so the owner keeps every decision. Static markup that the + translation pass rewrites (`data-i18n*`) stays outside `v-if` and + `v-for`, because re-rendering it would bring back the English; component + text uses `t()` instead. Order: Scenes (done: `SceneOptions` and + `SceneShotList` over the `gev-scenes` store), Weather, Recent imagery, Street level, CCTV, Layers, Display, Context and Radio, the command dock, the phone shell, then Cockpit. Layers that write into the right rail directly gain a slot instead. diff --git a/scripts/i18n.mjs b/scripts/i18n.mjs index f11377bf9..e5ed0c587 100644 --- a/scripts/i18n.mjs +++ b/scripts/i18n.mjs @@ -431,13 +431,19 @@ function markupFiles() { ); } -/** A file's markup; for a single-file component, its template block. */ +/** + * A file's markup; for a single-file component, its template block without + * interpolations, which are code: their t() calls are read with the sources. + */ function readMarkup(file) { const source = readFileSync(path.join(ROOT, file), 'utf8'); if (!file.endsWith('.vue')) return source; const start = source.indexOf(''); - return start === -1 ? '' : source.slice(start + '