diff --git a/.changeset/heavy-windows-mate.md b/.changeset/heavy-windows-mate.md new file mode 100644 index 00000000000..e0e9d0fe490 --- /dev/null +++ b/.changeset/heavy-windows-mate.md @@ -0,0 +1,5 @@ +--- +'create-qwik': patch +--- + +FIX: fix up vscode settings merge. Use JSON5 to parse settings.json to prevent parsing errors. diff --git a/packages/qwik/package.json b/packages/qwik/package.json index fa66a5abbf3..5e986adea48 100644 --- a/packages/qwik/package.json +++ b/packages/qwik/package.json @@ -30,6 +30,7 @@ }, "devDependencies": { "@builder.io/qwik": "workspace:^", + "@croct/json5-parser": "0.1.1", "domino": "2.1.6", "ignore": "5.3.1", "image-size": "1.1.1", diff --git a/packages/qwik/src/cli/add/update-files.ts b/packages/qwik/src/cli/add/update-files.ts index 875c8dce0d0..fea08c55ed0 100644 --- a/packages/qwik/src/cli/add/update-files.ts +++ b/packages/qwik/src/cli/add/update-files.ts @@ -1,3 +1,4 @@ +import { JsonParser, JsonObjectNode } from '@croct/json5-parser'; import fs from 'node:fs'; import { extname, join } from 'node:path'; import type { FsUpdates, UpdateAppOptions } from '../types'; @@ -28,8 +29,8 @@ export async function mergeIntegrationDir( if (destName === 'package.json') { await mergePackageJsons(fileUpdates, srcChildPath, destRootPath); - } else if (destName === 'settings.json') { - await mergeJsons(fileUpdates, srcChildPath, finalDestPath); + } else if (destDir.endsWith('.vscode') && destName === 'settings.json') { + await mergeVSCodeSettings(fileUpdates, srcChildPath, finalDestPath); } else if (destName === 'README.md') { await mergeReadmes(fileUpdates, srcChildPath, finalDestPath); } else if ( @@ -110,16 +111,19 @@ async function mergePackageJsons(fileUpdates: FsUpdates, srcPath: string, destPa } } -async function mergeJsons(fileUpdates: FsUpdates, srcPath: string, destPath: string) { +async function mergeVSCodeSettings(fileUpdates: FsUpdates, srcPath: string, destPath: string) { const srcContent = await fs.promises.readFile(srcPath, 'utf-8'); try { - const srcPkgJson = JSON.parse(srcContent); - const destPkgJson = JSON.parse(await fs.promises.readFile(destPath, 'utf-8')); - Object.assign(srcPkgJson, destPkgJson); + const srcPkgJson = JsonParser.parse(srcContent, JsonObjectNode); + const destPkgJson = JsonParser.parse( + await fs.promises.readFile(destPath, 'utf-8'), + JsonObjectNode + ); + destPkgJson.update({ ...destPkgJson.toJSON(), ...srcPkgJson.toJSON() }); fileUpdates.files.push({ path: destPath, - content: JSON.stringify(srcPkgJson, null, 2) + '\n', + content: destPkgJson.toString() + '\n', type: 'modify', }); } catch (e) { diff --git a/packages/qwik/src/cli/add/update-files.unit.ts b/packages/qwik/src/cli/add/update-files.unit.ts index 47b9b4c8f9b..a9124795d4c 100644 --- a/packages/qwik/src/cli/add/update-files.unit.ts +++ b/packages/qwik/src/cli/add/update-files.unit.ts @@ -1,3 +1,4 @@ +import { JsonObjectNode, JsonParser } from '@croct/json5-parser'; import { fs } from 'memfs'; import { join } from 'path'; import { describe, expect, test, vi } from 'vitest'; @@ -39,6 +40,16 @@ function createFakeFiles(dir: string) { fs.writeFileSync(join(dir, 'fake.ts'), 'fake file'); fs.writeFileSync(join(dir, 'package.json'), '{"name": "fake"}'); fs.writeFileSync(join(dir, 'src', 'global.css'), 'p{color: red}'); + fs.mkdirSync(join(dir, '.vscode'), { recursive: true }); + const settings = JsonParser.parse( + `{ + // Comment + "name": "John Doe", + "age": 42, + }`, + JsonObjectNode + ); + fs.writeFileSync(join(dir, '.vscode', 'settings.json'), settings.toString()); } describe('mergeIntegrationDir', () => { @@ -51,6 +62,7 @@ describe('mergeIntegrationDir', () => { const expectedResults = [ 'destDir/subDestDir/fake.ts', 'destDir/subDestDir/package.json', + 'destDir/subDestDir/.vscode/settings.json', 'destDir/subDestDir/src/global.css', ]; @@ -64,6 +76,18 @@ describe('mergeIntegrationDir', () => { const monorepoSubDir = join(fakeDestDir, 'apps', 'subpackage', 'src'); fs.mkdirSync(monorepoSubDir, { recursive: true }); fs.writeFileSync(join(monorepoSubDir, 'global.css'), '/* CSS */'); + const settings = JsonParser.parse( + `{ + // Comment Foo + "css.lint.unknownAtRules": "ignore" + }`, + JsonObjectNode + ); + fs.mkdirSync(join(fakeDestDir, 'apps', 'subpackage', '.vscode')); + fs.writeFileSync( + join(fakeDestDir, 'apps', 'subpackage', '.vscode', 'settings.json'), + settings.toString() + ); // Add a file that should stay in the root fs.writeFileSync(join(fakeSrcDir, 'should-stay-in-root.ts'), 'fake file'); @@ -84,6 +108,7 @@ describe('mergeIntegrationDir', () => { `destDir/subDestDir/should-stay-in-root.ts`, `destDir/subDestDir/package.json`, `destDir/subDestDir/should-stay/should-also-stay.ts`, + 'destDir/subDestDir/apps/subpackage/.vscode/settings.json', `destDir/subDestDir/apps/subpackage/src/global.css`, ]; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f1653307360..d99346b11a5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -569,6 +569,9 @@ importers: '@builder.io/qwik': specifier: workspace:^ version: 'link:' + '@croct/json5-parser': + specifier: 0.1.1 + version: 0.1.1 domino: specifier: 2.1.6 version: 2.1.6(patch_hash=cfc92e4c7200dc9749feafd71636da4534f892821d01a38afe3025620807877d) @@ -1071,6 +1074,12 @@ packages: resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} engines: {node: '>=0.1.90'} + '@croct/json5-parser@0.1.1': + resolution: {integrity: sha512-7+yo8Kd5JJp+VF3bjYGXW0S1s3zOF6IiJczmQy1d+t/pFn8bo7kJcbzRP+C78kcqZoBW4gs5H/c7ZtsH5DDb7w==} + + '@croct/json@2.1.0': + resolution: {integrity: sha512-UrWfjNQVlBxN+OVcFwHmkjARMW55MBN04E9KfGac8ac8z1QnFVuiOOFtMWXCk3UwsyRqhsNaFoYLZC+xxqsVjQ==} + '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -10572,6 +10581,12 @@ snapshots: '@colors/colors@1.6.0': {} + '@croct/json5-parser@0.1.1': + dependencies: + '@croct/json': 2.1.0 + + '@croct/json@2.1.0': {} + '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9