knip 2.19.9 → 2.19.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/DependencyDeputy.d.ts +1 -1
- package/dist/DependencyDeputy.js +15 -12
- package/dist/ProjectPrincipal.js +4 -5
- package/dist/binaries/bash-parser.js +38 -42
- package/dist/binaries/resolvers/npx.js +1 -2
- package/dist/binaries/resolvers/rollup.js +1 -2
- package/dist/typescript/ast-helpers.d.ts +1 -0
- package/dist/typescript/ast-helpers.js +18 -1
- package/dist/typescript/visitors/exports/exportAssignment.js +2 -1
- package/dist/typescript/visitors/exports/exportKeyword.js +1 -2
- package/dist/typescript/visitors/exports/moduleExportsAccessExpression.js +1 -2
- package/dist/typescript/visitors/scripts/execa.js +1 -1
- package/dist/typescript/visitors/scripts/zx.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -3
- package/dist/util/string.d.ts +0 -1
- package/dist/util/string.js +0 -18
|
@@ -45,7 +45,7 @@ export declare class DependencyDeputy {
|
|
|
45
45
|
addReferencedDependency(workspaceName: string, packageName: string): void;
|
|
46
46
|
addReferencedBinary(workspaceName: string, binaryName: string): void;
|
|
47
47
|
addPeerDependencies(workspaceName: string, peerDependencies: Map<string, Set<string>>): void;
|
|
48
|
-
|
|
48
|
+
getPeerDependenciesOf(workspaceName: string, dependency: string): string[];
|
|
49
49
|
maybeAddReferencedExternalDependency(workspace: Workspace, packageName: string): boolean;
|
|
50
50
|
maybeAddReferencedBinary(workspace: Workspace, binaryName: string): boolean;
|
|
51
51
|
private isInDependencies;
|
package/dist/DependencyDeputy.js
CHANGED
|
@@ -76,7 +76,7 @@ export class DependencyDeputy {
|
|
|
76
76
|
addPeerDependencies(workspaceName, peerDependencies) {
|
|
77
77
|
this.peerDependencies.set(workspaceName, peerDependencies);
|
|
78
78
|
}
|
|
79
|
-
|
|
79
|
+
getPeerDependenciesOf(workspaceName, dependency) {
|
|
80
80
|
return Array.from(this.peerDependencies.get(workspaceName)?.get(dependency) ?? []);
|
|
81
81
|
}
|
|
82
82
|
maybeAddReferencedExternalDependency(workspace, packageName) {
|
|
@@ -150,35 +150,38 @@ export class DependencyDeputy {
|
|
|
150
150
|
return true;
|
|
151
151
|
};
|
|
152
152
|
const peerDepRecs = {};
|
|
153
|
-
const
|
|
153
|
+
const isReferencedDependency = (dependency, isPeerDep) => {
|
|
154
154
|
if (referencedDependencies?.has(dependency))
|
|
155
|
-
return false;
|
|
156
|
-
if (isPeerDep && peerDepRecs[dependency])
|
|
157
155
|
return true;
|
|
156
|
+
if (isPeerDep && peerDepRecs[dependency])
|
|
157
|
+
return false;
|
|
158
158
|
const [scope, typedDependency] = dependency.split('/');
|
|
159
159
|
if (scope === '@types') {
|
|
160
160
|
const typedPackageName = getPackageFromDefinitelyTyped(typedDependency);
|
|
161
161
|
if (IGNORE_DEFINITELY_TYPED.includes(typedPackageName))
|
|
162
|
-
return
|
|
163
|
-
const peerDependencies = this.
|
|
162
|
+
return true;
|
|
163
|
+
const peerDependencies = this.getPeerDependenciesOf(workspaceName, typedPackageName);
|
|
164
164
|
if (peerDependencies.length) {
|
|
165
|
-
return
|
|
165
|
+
return !!peerDependencies.find(peerDependency => isReferencedDependency(peerDependency, true));
|
|
166
166
|
}
|
|
167
|
-
|
|
167
|
+
if (!referencedDependencies)
|
|
168
|
+
return false;
|
|
169
|
+
return referencedDependencies.has(typedPackageName);
|
|
168
170
|
}
|
|
169
|
-
const peerDependencies = this.
|
|
171
|
+
const peerDependencies = this.getPeerDependenciesOf(workspaceName, dependency);
|
|
170
172
|
peerDependencies.forEach(dep => (!peerDepRecs[dep] ? (peerDepRecs[dep] = 1) : peerDepRecs[dep]++));
|
|
171
|
-
return
|
|
173
|
+
return !!peerDependencies.find(peerDependency => isReferencedDependency(peerDependency, true));
|
|
172
174
|
};
|
|
175
|
+
const isNotReferencedDependency = (dependency) => !isReferencedDependency(dependency);
|
|
173
176
|
const pd = this.getProductionDependencies(workspaceName);
|
|
174
177
|
const dd = this.getDevDependencies(workspaceName);
|
|
175
178
|
pd.filter(isNotIgnoredDependency)
|
|
176
179
|
.filter(isNotIgnoredBinary)
|
|
177
|
-
.filter(
|
|
180
|
+
.filter(isNotReferencedDependency)
|
|
178
181
|
.forEach(symbol => dependencyIssues.push({ type: 'dependencies', filePath: manifestPath, symbol }));
|
|
179
182
|
dd.filter(isNotIgnoredDependency)
|
|
180
183
|
.filter(isNotIgnoredBinary)
|
|
181
|
-
.filter(
|
|
184
|
+
.filter(isNotReferencedDependency)
|
|
182
185
|
.forEach(symbol => devDependencyIssues.push({ type: 'devDependencies', filePath: manifestPath, symbol }));
|
|
183
186
|
}
|
|
184
187
|
return { dependencyIssues, devDependencyIssues };
|
package/dist/ProjectPrincipal.js
CHANGED
|
@@ -158,8 +158,7 @@ export class ProjectPrincipal {
|
|
|
158
158
|
}
|
|
159
159
|
getHasReferences(filePath, exportedItem) {
|
|
160
160
|
const hasReferences = { external: false, internal: false };
|
|
161
|
-
const
|
|
162
|
-
const symbolReferences = this.findReferences(filePath, node).flatMap(f => f.references);
|
|
161
|
+
const symbolReferences = this.findReferences(filePath, exportedItem.pos).flatMap(f => f.references);
|
|
163
162
|
for (const reference of symbolReferences) {
|
|
164
163
|
if (reference.fileName === filePath) {
|
|
165
164
|
if (!reference.isDefinition) {
|
|
@@ -180,7 +179,7 @@ export class ProjectPrincipal {
|
|
|
180
179
|
.filter(member => {
|
|
181
180
|
if (this.isPublicExport(member))
|
|
182
181
|
return false;
|
|
183
|
-
const referencedSymbols = this.findReferences(filePath, member.
|
|
182
|
+
const referencedSymbols = this.findReferences(filePath, member.pos);
|
|
184
183
|
const files = referencedSymbols
|
|
185
184
|
.flatMap(refs => refs.references)
|
|
186
185
|
.filter(ref => !ref.isDefinition)
|
|
@@ -191,8 +190,8 @@ export class ProjectPrincipal {
|
|
|
191
190
|
})
|
|
192
191
|
.map(member => member.identifier);
|
|
193
192
|
}
|
|
194
|
-
findReferences(filePath,
|
|
195
|
-
return this.backend.lsFindReferences(filePath,
|
|
193
|
+
findReferences(filePath, pos) {
|
|
194
|
+
return this.backend.lsFindReferences(filePath, pos) ?? [];
|
|
196
195
|
}
|
|
197
196
|
isPublicExport(exportedItem) {
|
|
198
197
|
const tags = this.getJSDocTags(exportedItem);
|
|
@@ -1,55 +1,51 @@
|
|
|
1
|
-
import
|
|
2
|
-
import Bash from 'tree-sitter-bash';
|
|
1
|
+
import parse from '@ericcornelissen/bash-parser';
|
|
3
2
|
import { debugLogObject } from '../util/debug.js';
|
|
4
3
|
import * as FallbackResolver from './resolvers/fallback.js';
|
|
5
4
|
import * as KnownResolvers from './resolvers/index.js';
|
|
6
5
|
import { stripBinaryPath } from './util.js';
|
|
7
|
-
const parser = new Parser();
|
|
8
|
-
parser.setLanguage(Bash);
|
|
9
|
-
const getCommandsFromScript = (script) => {
|
|
10
|
-
const tree = parser.parse(script);
|
|
11
|
-
const commands = [];
|
|
12
|
-
const traverse = (node) => {
|
|
13
|
-
switch (node.type) {
|
|
14
|
-
case 'command': {
|
|
15
|
-
const commandNameIndex = node.children.findIndex(node => node.type === 'command_name');
|
|
16
|
-
const command = node.children.slice(commandNameIndex).map(node => node.text);
|
|
17
|
-
commands.push(command);
|
|
18
|
-
break;
|
|
19
|
-
}
|
|
20
|
-
default:
|
|
21
|
-
break;
|
|
22
|
-
}
|
|
23
|
-
for (const child of node.children) {
|
|
24
|
-
traverse(child);
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
traverse(tree.rootNode);
|
|
28
|
-
return commands;
|
|
29
|
-
};
|
|
30
6
|
export const getBinariesFromScript = (script, { cwd, manifest, knownGlobalsOnly = false }) => {
|
|
31
7
|
if (!script)
|
|
32
8
|
return [];
|
|
33
9
|
const fromArgs = (args) => getBinariesFromScript(args.filter(arg => arg !== '--').join(' '), { cwd, manifest });
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
10
|
+
const getBinariesFromNodes = (nodes) => nodes.flatMap(node => {
|
|
11
|
+
switch (node.type) {
|
|
12
|
+
case 'Command': {
|
|
13
|
+
const binary = node.name?.text ? stripBinaryPath(node.name.text) : node.name?.text;
|
|
14
|
+
const commandExpansions = node.prefix?.flatMap(prefix => prefix.expansion?.filter(expansion => expansion.type === 'CommandExpansion') ?? []) ?? [];
|
|
15
|
+
if (commandExpansions.length > 0) {
|
|
16
|
+
return commandExpansions.flatMap(expansion => getBinariesFromNodes(expansion.commandAST.commands)) ?? [];
|
|
17
|
+
}
|
|
18
|
+
if (!binary || binary === '.' || binary === 'source')
|
|
19
|
+
return [];
|
|
20
|
+
if (binary.startsWith('-') || binary.startsWith('"') || binary.startsWith('..'))
|
|
21
|
+
return [];
|
|
22
|
+
if (['bun', 'deno'].includes(binary))
|
|
23
|
+
return [];
|
|
24
|
+
const args = node.suffix?.map(arg => arg.text) ?? [];
|
|
25
|
+
if (['!', 'test'].includes(binary))
|
|
26
|
+
return fromArgs(args);
|
|
27
|
+
if (binary in KnownResolvers) {
|
|
28
|
+
return KnownResolvers[binary].resolve(binary, args, { cwd, manifest, fromArgs });
|
|
29
|
+
}
|
|
30
|
+
if (knownGlobalsOnly)
|
|
31
|
+
return [];
|
|
32
|
+
return FallbackResolver.resolve(binary, args, { cwd, manifest, fromArgs });
|
|
33
|
+
}
|
|
34
|
+
case 'LogicalExpression':
|
|
35
|
+
return getBinariesFromNodes([node.left, node.right]);
|
|
36
|
+
case 'If':
|
|
37
|
+
return getBinariesFromNodes([...node.clause.commands, ...node.then.commands, ...(node.else?.commands ?? [])]);
|
|
38
|
+
case 'For':
|
|
39
|
+
return getBinariesFromNodes(node.do.commands);
|
|
40
|
+
case 'CompoundList':
|
|
41
|
+
return getBinariesFromNodes(node.commands);
|
|
42
|
+
default:
|
|
43
|
+
return [];
|
|
46
44
|
}
|
|
47
|
-
|
|
48
|
-
return [];
|
|
49
|
-
return FallbackResolver.resolve(binary, args, { cwd, manifest, fromArgs });
|
|
50
|
-
};
|
|
45
|
+
});
|
|
51
46
|
try {
|
|
52
|
-
|
|
47
|
+
const parsed = parse(script);
|
|
48
|
+
return parsed?.commands ? getBinariesFromNodes(parsed.commands) : [];
|
|
53
49
|
}
|
|
54
50
|
catch (error) {
|
|
55
51
|
debugLogObject('Bash parser error', error);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import parseArgs from 'minimist';
|
|
2
2
|
import { isInternal } from '../../util/path.js';
|
|
3
|
-
import { stripQuotes } from '../../util/string.js';
|
|
4
3
|
import { getBinariesFromScript } from '../bash-parser.js';
|
|
5
4
|
import { argsFrom, stripVersionFromSpecifier, toBinary } from '../util.js';
|
|
6
5
|
export const resolve = (binary, args, { cwd, fromArgs, manifest }) => {
|
|
@@ -11,7 +10,7 @@ export const resolve = (binary, args, { cwd, fromArgs, manifest }) => {
|
|
|
11
10
|
const packageSpecifier = parsed._[0];
|
|
12
11
|
const specifier = packageSpecifier ? stripVersionFromSpecifier(packageSpecifier) : '';
|
|
13
12
|
const packages = parsed.package ? [parsed.package].flat().map(stripVersionFromSpecifier) : [];
|
|
14
|
-
const command = parsed.call ? getBinariesFromScript(
|
|
13
|
+
const command = parsed.call ? getBinariesFromScript(parsed.call, { cwd, manifest }) : [];
|
|
15
14
|
const restArgs = argsFrom(args, packageSpecifier);
|
|
16
15
|
const dependencies = manifest ? Object.keys({ ...manifest.dependencies, ...manifest.devDependencies }) : [];
|
|
17
16
|
const isBinary = specifier && !packageSpecifier.includes('@') && !isInternal(specifier) && !dependencies.includes(specifier);
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import parseArgs from 'minimist';
|
|
2
2
|
import { compact } from '../../util/array.js';
|
|
3
|
-
import { stripQuotes } from '../../util/string.js';
|
|
4
3
|
import { toBinary, tryResolveSpecifiers } from '../util.js';
|
|
5
4
|
export const resolve = (binary, args, { cwd, fromArgs }) => {
|
|
6
5
|
const safeArgs = args.filter(arg => arg !== '--watch');
|
|
7
6
|
const parsed = parseArgs(safeArgs, { alias: { plugin: 'p' } });
|
|
8
|
-
const watchers = parsed.watch ? fromArgs(Object.values(parsed.watch)
|
|
7
|
+
const watchers = parsed.watch ? fromArgs(Object.values(parsed.watch)) : [];
|
|
9
8
|
const plugins = parsed.plugin ? tryResolveSpecifiers(cwd, [parsed.plugin].flat()) : [];
|
|
10
9
|
const configPlugins = parsed.configPlugin ? tryResolveSpecifiers(cwd, [parsed.configPlugin].flat()) : [];
|
|
11
10
|
return compact([toBinary(binary), ...watchers, ...plugins, ...configPlugins]);
|
|
@@ -18,6 +18,7 @@ type LiteralLikeElementAccessExpression = ts.ElementAccessExpression & ts.Declar
|
|
|
18
18
|
export declare function isModuleExportsAccessExpression(node: ts.Node): node is LiteralLikeElementAccessExpression & {
|
|
19
19
|
expression: ts.Identifier;
|
|
20
20
|
};
|
|
21
|
+
export declare function stripQuotes(name: string): string;
|
|
21
22
|
export declare function findAncestor<T>(node: ts.Node | undefined, callback: (element: ts.Node) => boolean | 'STOP'): T | undefined;
|
|
22
23
|
export declare function findDescendants<T>(node: ts.Node | undefined, callback: (element: ts.Node) => boolean | 'STOP'): T[];
|
|
23
24
|
export declare const isDeclarationFileExtension: (extension: string) => boolean;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
|
-
import { stripQuotes } from '../util/string.js';
|
|
3
2
|
export function isValidImportTypeNode(node) {
|
|
4
3
|
return ts.isImportTypeNode(node);
|
|
5
4
|
}
|
|
@@ -40,6 +39,24 @@ export function isModuleExportsAccessExpression(node) {
|
|
|
40
39
|
node.expression.escapedText === 'module' &&
|
|
41
40
|
getAccessExpressionName(node) === 'exports');
|
|
42
41
|
}
|
|
42
|
+
export function stripQuotes(name) {
|
|
43
|
+
const length = name.length;
|
|
44
|
+
if (length >= 2 && name.charCodeAt(0) === name.charCodeAt(length - 1) && isQuoteOrBacktick(name.charCodeAt(0))) {
|
|
45
|
+
return name.substring(1, length - 1);
|
|
46
|
+
}
|
|
47
|
+
return name;
|
|
48
|
+
}
|
|
49
|
+
var CharacterCodes;
|
|
50
|
+
(function (CharacterCodes) {
|
|
51
|
+
CharacterCodes[CharacterCodes["backtick"] = 96] = "backtick";
|
|
52
|
+
CharacterCodes[CharacterCodes["doubleQuote"] = 34] = "doubleQuote";
|
|
53
|
+
CharacterCodes[CharacterCodes["singleQuote"] = 39] = "singleQuote";
|
|
54
|
+
})(CharacterCodes || (CharacterCodes = {}));
|
|
55
|
+
function isQuoteOrBacktick(charCode) {
|
|
56
|
+
return (charCode === CharacterCodes.singleQuote ||
|
|
57
|
+
charCode === CharacterCodes.doubleQuote ||
|
|
58
|
+
charCode === CharacterCodes.backtick);
|
|
59
|
+
}
|
|
43
60
|
export function findAncestor(node, callback) {
|
|
44
61
|
node = node?.parent;
|
|
45
62
|
while (node) {
|
|
@@ -3,6 +3,7 @@ import { SymbolType } from '../../../types/issues.js';
|
|
|
3
3
|
import { exportVisitor as visit } from '../index.js';
|
|
4
4
|
export default visit(() => true, node => {
|
|
5
5
|
if (ts.isExportAssignment(node)) {
|
|
6
|
-
|
|
6
|
+
const pos = node.getChildAt(1).getStart();
|
|
7
|
+
return { node, identifier: 'default', type: SymbolType.UNKNOWN, pos };
|
|
7
8
|
}
|
|
8
9
|
});
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
import { SymbolType } from '../../../types/issues.js';
|
|
3
3
|
import { compact } from '../../../util/array.js';
|
|
4
|
-
import { stripQuotes } from '
|
|
5
|
-
import { isPrivateMember } from '../../ast-helpers.js';
|
|
4
|
+
import { isPrivateMember, stripQuotes } from '../../ast-helpers.js';
|
|
6
5
|
import { exportVisitor as visit } from '../index.js';
|
|
7
6
|
export default visit(() => true, node => {
|
|
8
7
|
const modifierKinds = node.modifiers?.map(modifier => modifier.kind) ?? [];
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
import { SymbolType } from '../../../types/issues.js';
|
|
3
|
-
import { stripQuotes } from '
|
|
4
|
-
import { isModuleExportsAccessExpression } from '../../ast-helpers.js';
|
|
3
|
+
import { isModuleExportsAccessExpression, stripQuotes } from '../../ast-helpers.js';
|
|
5
4
|
import { isJS } from '../helpers.js';
|
|
6
5
|
import { exportVisitor as visit } from '../index.js';
|
|
7
6
|
export default visit(isJS, node => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
|
-
import { stripQuotes } from '
|
|
2
|
+
import { stripQuotes } from '../../ast-helpers.js';
|
|
3
3
|
import { scriptVisitor as visit } from '../index.js';
|
|
4
4
|
export default visit(sourceFile => sourceFile.statements.some(statementImportsExeca$), node => {
|
|
5
5
|
if (ts.isTaggedTemplateExpression(node)) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
|
-
import { stripQuotes } from '
|
|
2
|
+
import { stripQuotes } from '../../ast-helpers.js';
|
|
3
3
|
import { scriptVisitor as visit } from '../index.js';
|
|
4
4
|
export default visit(sourceFile => ts.getShebang(sourceFile.text) === '#!/usr/bin/env zx', node => {
|
|
5
5
|
if (ts.isTaggedTemplateExpression(node) && node.tag.getText() === '$') {
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "2.19.
|
|
1
|
+
export declare const version = "2.19.11";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '2.19.
|
|
1
|
+
export const version = '2.19.11';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "knip",
|
|
3
|
-
"version": "2.19.
|
|
3
|
+
"version": "2.19.11",
|
|
4
4
|
"description": "Find unused files, dependencies and exports in your TypeScript and JavaScript projects",
|
|
5
5
|
"homepage": "https://github.com/webpro/knip",
|
|
6
6
|
"repository": "github:webpro/knip",
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"schema.json"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
+
"@ericcornelissen/bash-parser": "^0.5.2",
|
|
45
46
|
"@npmcli/map-workspaces": "^3.0.4",
|
|
46
47
|
"@snyk/github-codeowners": "^1.1.0",
|
|
47
48
|
"chalk": "^5.2.0",
|
|
@@ -55,8 +56,6 @@
|
|
|
55
56
|
"pretty-ms": "^8.0.0",
|
|
56
57
|
"strip-json-comments": "^5.0.0",
|
|
57
58
|
"summary": "^2.1.0",
|
|
58
|
-
"tree-sitter": "0.20.5",
|
|
59
|
-
"tree-sitter-bash": "0.19.0",
|
|
60
59
|
"typescript": "^5.0.2",
|
|
61
60
|
"zod": "^3.22.2",
|
|
62
61
|
"zod-validation-error": "^1.5.0"
|
package/dist/util/string.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function stripQuotes(name: string): string;
|
package/dist/util/string.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export function stripQuotes(name) {
|
|
2
|
-
const length = name.length;
|
|
3
|
-
if (length >= 2 && name.charCodeAt(0) === name.charCodeAt(length - 1) && isQuoteOrBacktick(name.charCodeAt(0))) {
|
|
4
|
-
return name.substring(1, length - 1);
|
|
5
|
-
}
|
|
6
|
-
return name;
|
|
7
|
-
}
|
|
8
|
-
var CharacterCodes;
|
|
9
|
-
(function (CharacterCodes) {
|
|
10
|
-
CharacterCodes[CharacterCodes["backtick"] = 96] = "backtick";
|
|
11
|
-
CharacterCodes[CharacterCodes["doubleQuote"] = 34] = "doubleQuote";
|
|
12
|
-
CharacterCodes[CharacterCodes["singleQuote"] = 39] = "singleQuote";
|
|
13
|
-
})(CharacterCodes || (CharacterCodes = {}));
|
|
14
|
-
function isQuoteOrBacktick(charCode) {
|
|
15
|
-
return (charCode === CharacterCodes.singleQuote ||
|
|
16
|
-
charCode === CharacterCodes.doubleQuote ||
|
|
17
|
-
charCode === CharacterCodes.backtick);
|
|
18
|
-
}
|