From 673643c19d5d87d311f30c77063fb9ec1b320d58 Mon Sep 17 00:00:00 2001
From: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com>
Date: Sun, 10 Apr 2022 18:08:34 +0200
Subject: [PATCH 1/2] Generate codeframe positions for JSON5
---
flow-libs/json-source-map.js.flow | 41 -------------------
packages/core/core/package.json | 2 +-
.../core/core/src/requests/EntryRequest.js | 4 +-
.../core/core/src/requests/TargetRequest.js | 4 +-
packages/core/diagnostic/package.json | 2 +-
packages/core/diagnostic/src/diagnostic.js | 9 ++--
packages/core/utils/src/schema.js | 2 +-
packages/transformers/babel/src/config.js | 7 +---
.../transformers/webextension/package.json | 4 +-
.../src/WebExtensionTransformer.js | 4 +-
.../transformers/webmanifest/package.json | 4 +-
.../webmanifest/src/WebManifestTransformer.js | 4 +-
yarn.lock | 36 +++++++++++-----
13 files changed, 46 insertions(+), 77 deletions(-)
delete mode 100644 flow-libs/json-source-map.js.flow
diff --git a/flow-libs/json-source-map.js.flow b/flow-libs/json-source-map.js.flow
deleted file mode 100644
index eda1c8a14b2..00000000000
--- a/flow-libs/json-source-map.js.flow
+++ /dev/null
@@ -1,41 +0,0 @@
-//@flow strict-local
-
-// Derived from the README and source of json-source-map located at
-// https://github.com/epoberezkin/json-source-map and
-// https://github.com/epoberezkin/json-source-map/blob/v0.6.1/index.js
-// Which is licensed MIT
-
-declare module 'json-source-map' {
- declare type Position = {|
- line: number,
- column: number,
- pos: number,
- |};
-
- declare export type Mapping = {|
- value: Position,
- valueEnd: Position,
- key?: Position,
- keyEnd?: Position,
- |};
-
- declare export default {|
- parse: (
- json: string,
- _?: mixed,
- opts?: {|bigint: boolean|},
- ) => {|
- data: any,
- pointers: {|[key: string]: Mapping|},
- |},
-
- stringify: (
- data: any,
- _?: mixed,
- space?: string | number | {|space: number, es6: boolean|},
- ) => {|
- json: string,
- pointers: {|[key: string]: Mapping|},
- |},
- |};
-}
diff --git a/packages/core/core/package.json b/packages/core/core/package.json
index 04748a7f420..a757f27d24e 100644
--- a/packages/core/core/package.json
+++ b/packages/core/core/package.json
@@ -24,6 +24,7 @@
"check-ts": "tsc --noEmit index.d.ts"
},
"dependencies": {
+ "@mischnic/json-sourcemap": "^0.1.0",
"@parcel/cache": "2.4.1",
"@parcel/diagnostic": "2.4.1",
"@parcel/events": "2.4.1",
@@ -43,7 +44,6 @@
"clone": "^2.1.1",
"dotenv": "^7.0.0",
"dotenv-expand": "^5.1.0",
- "json-source-map": "^0.6.1",
"json5": "^2.2.0",
"msgpackr": "^1.5.4",
"nullthrows": "^1.1.1",
diff --git a/packages/core/core/src/requests/EntryRequest.js b/packages/core/core/src/requests/EntryRequest.js
index 2f4031e3315..a26f8abc8a8 100644
--- a/packages/core/core/src/requests/EntryRequest.js
+++ b/packages/core/core/src/requests/EntryRequest.js
@@ -17,7 +17,7 @@ import ThrowableDiagnostic, {
getJSONSourceLocation,
} from '@parcel/diagnostic';
import path from 'path';
-import jsonMap, {type Mapping} from 'json-source-map';
+import {parse, type Mapping} from '@mischnic/json-sourcemap';
import {
type ProjectPath,
fromProjectPath,
@@ -345,7 +345,7 @@ export class EntryResolver {
return {
...pkg,
filePath: pkgFile,
- map: jsonMap.parse(content.replace(/\t/g, ' ')),
+ map: parse(content, undefined, {tabWidth: 1}),
};
}
}
diff --git a/packages/core/core/src/requests/TargetRequest.js b/packages/core/core/src/requests/TargetRequest.js
index 7656169a806..f21d9bcdd95 100644
--- a/packages/core/core/src/requests/TargetRequest.js
+++ b/packages/core/core/src/requests/TargetRequest.js
@@ -34,7 +34,7 @@ import createParcelConfigRequest, {
} from './ParcelConfigRequest';
// $FlowFixMe
import browserslist from 'browserslist';
-import jsonMap from 'json-source-map';
+import {parse} from '@mischnic/json-sourcemap';
import invariant from 'assert';
import nullthrows from 'nullthrows';
import {
@@ -395,7 +395,7 @@ export class TargetResolver {
let _pkgFilePath = (pkgFilePath = pkgFile.filePath); // For Flow
pkgDir = path.dirname(_pkgFilePath);
pkgContents = await this.fs.readFile(_pkgFilePath, 'utf8');
- pkgMap = jsonMap.parse(pkgContents.replace(/\t/g, ' '));
+ pkgMap = parse(pkgContents, undefined, {tabWidth: 1});
let pp = toProjectPath(this.options.projectRoot, _pkgFilePath);
this.api.invalidateOnFileUpdate(pp);
diff --git a/packages/core/diagnostic/package.json b/packages/core/diagnostic/package.json
index 40036f545e7..963023c6d11 100644
--- a/packages/core/diagnostic/package.json
+++ b/packages/core/diagnostic/package.json
@@ -24,7 +24,7 @@
"check-ts": "tsc --noEmit lib/diagnostic.d.ts"
},
"dependencies": {
- "json-source-map": "^0.6.1",
+ "@mischnic/json-sourcemap": "^0.1.0",
"nullthrows": "^1.1.1"
}
}
diff --git a/packages/core/diagnostic/src/diagnostic.js b/packages/core/diagnostic/src/diagnostic.js
index c59f9884871..fe436925654 100644
--- a/packages/core/diagnostic/src/diagnostic.js
+++ b/packages/core/diagnostic/src/diagnostic.js
@@ -2,7 +2,7 @@
import invariant from 'assert';
import nullthrows from 'nullthrows';
-import jsonMap, {type Mapping} from 'json-source-map';
+import {parse, type Mapping} from '@mischnic/json-sourcemap';
/** These positions are 1-based (so 1
is the first line/column) */
export type DiagnosticHighlightLocation = {|
@@ -231,9 +231,10 @@ export function generateJSONCodeHighlights(
|},
ids: Array<{|key: string, type?: ?'key' | 'value', message?: string|}>,
): Array {
- // json-source-map doesn't support a tabWidth option (yet)
let map =
- typeof data == 'string' ? jsonMap.parse(data.replace(/\t/g, ' ')) : data;
+ typeof data == 'string'
+ ? parse(data, undefined, {dialect: 'JSON5', tabWidth: 1})
+ : data;
return ids.map(({key, type, message}) => {
let pos = nullthrows(map.pointers[key]);
return {
@@ -276,7 +277,7 @@ export function getJSONSourceLocation(
/** Sanitizes object keys before using them as key
in generateJSONCodeHighlights */
export function encodeJSONKeyComponent(component: string): string {
- return component.replace(/\//g, '~1');
+ return component.replace(/~/g, '~0').replace(/\//g, '~1');
}
const escapeCharacters = ['\\', '*', '_', '~'];
diff --git a/packages/core/utils/src/schema.js b/packages/core/utils/src/schema.js
index 5004c13136a..b719a61c545 100644
--- a/packages/core/utils/src/schema.js
+++ b/packages/core/utils/src/schema.js
@@ -4,7 +4,7 @@ import ThrowableDiagnostic, {
escapeMarkdown,
encodeJSONKeyComponent,
} from '@parcel/diagnostic';
-import type {Mapping} from 'json-source-map';
+import type {Mapping} from '@mischnic/json-sourcemap';
import nullthrows from 'nullthrows';
// flowlint-next-line untyped-import:off
import levenshtein from 'fastest-levenshtein';
diff --git a/packages/transformers/babel/src/config.js b/packages/transformers/babel/src/config.js
index 15f858400e0..66490264242 100644
--- a/packages/transformers/babel/src/config.js
+++ b/packages/transformers/babel/src/config.js
@@ -394,12 +394,7 @@ async function getCodeHighlights(fs, filePath, redundantPresets) {
}
if (pointers.length > 0) {
- try {
- return generateJSONCodeHighlights(contents, pointers);
- } catch {
- // TODO: support code highlights for json5 sources.
- // Babel supports json5 syntax, but json-source-map does not.
- }
+ return generateJSONCodeHighlights(contents, pointers);
}
}
diff --git a/packages/transformers/webextension/package.json b/packages/transformers/webextension/package.json
index 7fb077bbb05..e9b77772f1a 100644
--- a/packages/transformers/webextension/package.json
+++ b/packages/transformers/webextension/package.json
@@ -19,10 +19,10 @@
"parcel": "^2.4.1"
},
"dependencies": {
+ "@mischnic/json-sourcemap": "^0.1.0",
"@parcel/diagnostic": "2.4.1",
"@parcel/plugin": "2.4.1",
"@parcel/utils": "2.4.1",
- "content-security-policy-parser": "^0.3.0",
- "json-source-map": "^0.6.1"
+ "content-security-policy-parser": "^0.3.0"
}
}
diff --git a/packages/transformers/webextension/src/WebExtensionTransformer.js b/packages/transformers/webextension/src/WebExtensionTransformer.js
index c409b21450d..00d72df7efe 100644
--- a/packages/transformers/webextension/src/WebExtensionTransformer.js
+++ b/packages/transformers/webextension/src/WebExtensionTransformer.js
@@ -3,7 +3,7 @@ import type {MutableAsset} from '@parcel/types';
import {Transformer} from '@parcel/plugin';
import path from 'path';
-import jsm from 'json-source-map';
+import {parse} from '@mischnic/json-sourcemap';
import parseCSP from 'content-security-policy-parser';
import {validateSchema} from '@parcel/utils';
import ThrowableDiagnostic, {
@@ -267,7 +267,7 @@ function cspPatchHMR(policy: ?string) {
export default (new Transformer({
async transform({asset, options}) {
const code = await asset.getCode();
- const parsed = jsm.parse(code);
+ const parsed = parse(code);
const data: any = parsed.data;
validateSchema.diagnostic(
WebExtensionSchema,
diff --git a/packages/transformers/webmanifest/package.json b/packages/transformers/webmanifest/package.json
index 18be015c3ca..005b74800ef 100644
--- a/packages/transformers/webmanifest/package.json
+++ b/packages/transformers/webmanifest/package.json
@@ -19,9 +19,9 @@
"parcel": "^2.4.1"
},
"dependencies": {
+ "@mischnic/json-sourcemap": "^0.1.0",
"@parcel/diagnostic": "2.4.1",
"@parcel/plugin": "2.4.1",
- "@parcel/utils": "2.4.1",
- "json-source-map": "^0.6.1"
+ "@parcel/utils": "2.4.1"
}
}
diff --git a/packages/transformers/webmanifest/src/WebManifestTransformer.js b/packages/transformers/webmanifest/src/WebManifestTransformer.js
index adefd04948c..138c90f3a76 100644
--- a/packages/transformers/webmanifest/src/WebManifestTransformer.js
+++ b/packages/transformers/webmanifest/src/WebManifestTransformer.js
@@ -3,7 +3,7 @@
import type {SchemaEntity} from '@parcel/utils';
import invariant from 'assert';
-import jsm from 'json-source-map';
+import {parse} from '@mischnic/json-sourcemap';
import {getJSONSourceLocation} from '@parcel/diagnostic';
import {Transformer} from '@parcel/plugin';
import {validateSchema} from '@parcel/utils';
@@ -36,7 +36,7 @@ const MANIFEST_SCHEMA: SchemaEntity = {
export default (new Transformer({
async transform({asset}) {
const source = await asset.getCode();
- const {data, pointers} = jsm.parse(source);
+ const {data, pointers} = parse(source);
validateSchema.diagnostic(
MANIFEST_SCHEMA,
diff --git a/yarn.lock b/yarn.lock
index bafcf838a20..f00b1a05290 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1853,6 +1853,18 @@
npmlog "^4.1.2"
write-file-atomic "^2.3.0"
+"@lezer/common@^0.15.0", "@lezer/common@^0.15.7":
+ version "0.15.12"
+ resolved "https://registry.yarnpkg.com/@lezer/common/-/common-0.15.12.tgz#2f21aec551dd5fd7d24eb069f90f54d5bc6ee5e9"
+ integrity sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig==
+
+"@lezer/lr@^0.15.4":
+ version "0.15.8"
+ resolved "https://registry.yarnpkg.com/@lezer/lr/-/lr-0.15.8.tgz#1564a911e62b0a0f75ca63794a6aa8c5dc63db21"
+ integrity sha512-bM6oE6VQZ6hIFxDNKk8bKPa14hqFrV07J/vHGOeiAbJReIaQXmkVb6xQu4MR+JBTLa5arGRyAAjJe1qaQt3Uvg==
+ dependencies:
+ "@lezer/common" "^0.15.0"
+
"@mdx-js/mdx@^1.6.22":
version "1.6.22"
resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.6.22.tgz#8a723157bf90e78f17dc0f27995398e6c731f1ba"
@@ -1888,6 +1900,15 @@
resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.22.tgz#219dfd89ae5b97a8801f015323ffa4b62f45718b"
integrity sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==
+"@mischnic/json-sourcemap@^0.1.0":
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/@mischnic/json-sourcemap/-/json-sourcemap-0.1.0.tgz#38af657be4108140a548638267d02a2ea3336507"
+ integrity sha512-dQb3QnfNqmQNYA4nFSN/uLaByIic58gOXq4Y4XqLOWmOrw73KmJPt/HLyG0wvn1bnR6mBKs/Uwvkh+Hns1T0XA==
+ dependencies:
+ "@lezer/common" "^0.15.7"
+ "@lezer/lr" "^0.15.4"
+ json5 "^2.2.1"
+
"@mrmlnc/readdir-enhanced@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
@@ -7998,11 +8019,6 @@ json-schema@0.2.3:
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
-json-source-map@^0.6.1:
- version "0.6.1"
- resolved "https://registry.yarnpkg.com/json-source-map/-/json-source-map-0.6.1.tgz#e0b1f6f4ce13a9ad57e2ae165a24d06e62c79a0f"
- integrity sha512-1QoztHPsMQqhDq0hlXY5ZqcEdUzxQEIxgFkKl4WUp2pgShObl+9ovi4kRh2TfvAfxAoHOJ9vIMEqk3k4iex7tg==
-
json-stable-stringify-without-jsonify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
@@ -8020,12 +8036,10 @@ json5@^1.0.1:
dependencies:
minimist "^1.2.0"
-json5@^2.1.2, json5@^2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3"
- integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==
- dependencies:
- minimist "^1.2.5"
+json5@^2.1.2, json5@^2.2.0, json5@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c"
+ integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==
jsonfile@^4.0.0:
version "4.0.0"
From 4910ce15c0b64c1a5c2f0d6bec683b4bac57e880 Mon Sep 17 00:00:00 2001
From: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com>
Date: Sun, 10 Apr 2022 18:30:05 +0200
Subject: [PATCH 2/2] Add tests for JSON5 parcelrc and babelrc
---
.../core/test/ParcelConfigRequest.test.js | 39 +++++++++++++
.../config-extends-not-found/.parcelrc-json5 | 3 +
packages/core/diagnostic/src/diagnostic.js | 6 +-
packages/core/integration-tests/test/babel.js | 56 +++++++++++++++++++
.../babel-warn-some-json5/.babelrc | 4 ++
.../babel-warn-some-json5/index.js | 0
.../babel-warn-some-json5/yarn.lock | 0
7 files changed, 105 insertions(+), 3 deletions(-)
create mode 100644 packages/core/core/test/fixtures/config-extends-not-found/.parcelrc-json5
create mode 100644 packages/core/integration-tests/test/integration/babel-warn-some-json5/.babelrc
create mode 100644 packages/core/integration-tests/test/integration/babel-warn-some-json5/index.js
create mode 100644 packages/core/integration-tests/test/integration/babel-warn-some-json5/yarn.lock
diff --git a/packages/core/core/test/ParcelConfigRequest.test.js b/packages/core/core/test/ParcelConfigRequest.test.js
index 8784a3d0143..f9496685773 100644
--- a/packages/core/core/test/ParcelConfigRequest.test.js
+++ b/packages/core/core/test/ParcelConfigRequest.test.js
@@ -813,6 +813,45 @@ describe('ParcelConfigRequest', () => {
);
});
+ it('should emit a codeframe when an extended parcel config file is not found in JSON5', async () => {
+ let configFilePath = path.join(
+ __dirname,
+ 'fixtures',
+ 'config-extends-not-found',
+ '.parcelrc-json5',
+ );
+ let code = await DEFAULT_OPTIONS.inputFS.readFile(configFilePath, 'utf8');
+
+ // $FlowFixMe[prop-missing]
+ await assert.rejects(
+ () => parseAndProcessConfig(configFilePath, code, DEFAULT_OPTIONS),
+ {
+ name: 'Error',
+ diagnostics: [
+ {
+ message: 'Cannot find extended parcel config',
+ origin: '@parcel/core',
+ codeFrames: [
+ {
+ filePath: configFilePath,
+ language: 'json5',
+ code,
+ codeHighlights: [
+ {
+ message:
+ '"./.parclrc-node-modules" does not exist, did you mean "./.parcelrc-node-modules"?',
+ start: {line: 2, column: 12},
+ end: {line: 2, column: 36},
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ );
+ });
+
it('should emit a codeframe when an extended parcel config node module is not found', async () => {
let configFilePath = path.join(
__dirname,
diff --git a/packages/core/core/test/fixtures/config-extends-not-found/.parcelrc-json5 b/packages/core/core/test/fixtures/config-extends-not-found/.parcelrc-json5
new file mode 100644
index 00000000000..03464dc7fc8
--- /dev/null
+++ b/packages/core/core/test/fixtures/config-extends-not-found/.parcelrc-json5
@@ -0,0 +1,3 @@
+{
+ extends: "./.parclrc-node-modules"
+}
diff --git a/packages/core/diagnostic/src/diagnostic.js b/packages/core/diagnostic/src/diagnostic.js
index fe436925654..8ab4c398147 100644
--- a/packages/core/diagnostic/src/diagnostic.js
+++ b/packages/core/diagnostic/src/diagnostic.js
@@ -215,8 +215,8 @@ export default class ThrowableDiagnostic extends Error {
}
/**
- * Turns a list of positions in a JSON file with messages into a list of diagnostics.
- * Uses epoberezkin/json-source-map.
+ * Turns a list of positions in a JSON5 file with messages into a list of diagnostics.
+ * Uses @mischnic/json-sourcemap.
*
* @param code the JSON code
* @param ids A list of JSON keypaths (key: "/some/parent/child"
) with corresponding messages, \
@@ -245,7 +245,7 @@ export function generateJSONCodeHighlights(
}
/**
- * Converts entries in epoberezkin/json-source-map's
+ * Converts entries in @mischnic/json-sourcemap's
* result.pointers
array.
*/
export function getJSONSourceLocation(
diff --git a/packages/core/integration-tests/test/babel.js b/packages/core/integration-tests/test/babel.js
index cb664a1b979..cbd333f9cf3 100644
--- a/packages/core/integration-tests/test/babel.js
+++ b/packages/core/integration-tests/test/babel.js
@@ -711,4 +711,60 @@ describe('babel', function () {
},
]);
});
+
+ it('should warn when a JSON5 babel config contains redundant plugins', async function () {
+ let messages = [];
+ let loggerDisposable = Logger.onLog(message => {
+ messages.push(message);
+ });
+ let filePath = path.join(
+ __dirname,
+ '/integration/babel-warn-some-json5/index.js',
+ );
+ await bundle(filePath);
+ loggerDisposable.dispose();
+
+ let babelrcPath = path.resolve(path.dirname(filePath), '.babelrc');
+ assert.deepEqual(messages, [
+ {
+ type: 'log',
+ level: 'warn',
+ diagnostics: [
+ {
+ origin: '@parcel/transformer-babel',
+ message: md`Parcel includes transpilation by default. Babel config __${path.relative(
+ process.cwd(),
+ babelrcPath,
+ )}__ includes the following redundant presets: __@parcel/babel-preset-env__. Removing these may improve build performance.`,
+ codeFrames: [
+ {
+ filePath: babelrcPath,
+ codeHighlights: [
+ {
+ message: undefined,
+ start: {
+ line: 2,
+ column: 13,
+ },
+ end: {
+ line: 2,
+ column: 38,
+ },
+ },
+ ],
+ },
+ ],
+ hints: [
+ md`Remove the above presets from __${path.relative(
+ process.cwd(),
+ babelrcPath,
+ )}__`,
+ ],
+ documentationURL:
+ 'https://parceljs.org/languages/javascript/#default-presets',
+ },
+ ],
+ },
+ ]);
+ });
});
diff --git a/packages/core/integration-tests/test/integration/babel-warn-some-json5/.babelrc b/packages/core/integration-tests/test/integration/babel-warn-some-json5/.babelrc
new file mode 100644
index 00000000000..3da8adeca2a
--- /dev/null
+++ b/packages/core/integration-tests/test/integration/babel-warn-some-json5/.babelrc
@@ -0,0 +1,4 @@
+{
+ presets: ["@parcel/babel-preset-env"],
+ "plugins": ["../babelrc-custom/babel-plugin-dummy"]
+}
diff --git a/packages/core/integration-tests/test/integration/babel-warn-some-json5/index.js b/packages/core/integration-tests/test/integration/babel-warn-some-json5/index.js
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/packages/core/integration-tests/test/integration/babel-warn-some-json5/yarn.lock b/packages/core/integration-tests/test/integration/babel-warn-some-json5/yarn.lock
new file mode 100644
index 00000000000..e69de29bb2d