@metamask-previews/build-utils 3.0.4-preview-bfee350 → 3.0.4-preview-5c1b2b1
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/transforms/remove-fenced-code.cjs +0 -3
- package/dist/transforms/remove-fenced-code.cjs.map +1 -1
- package/dist/transforms/remove-fenced-code.d.cts.map +1 -1
- package/dist/transforms/remove-fenced-code.d.mts.map +1 -1
- package/dist/transforms/remove-fenced-code.mjs +0 -3
- package/dist/transforms/remove-fenced-code.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -156,7 +156,6 @@ function removeFencedCode(filePath, fileContent, featureLabels) {
|
|
|
156
156
|
// Forbid empty fences
|
|
157
157
|
const { line: previousLine, indices: previousIndices } =
|
|
158
158
|
// We're only in this case if i > 0, so this will always be defined.
|
|
159
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
160
159
|
parsedDirectives[i - 1];
|
|
161
160
|
if (fileContent.substring(previousIndices[1], indices[0]).trim() === '') {
|
|
162
161
|
throw new Error(`Empty fence found in file "${filePath}":\n${previousLine}\n${line}\n`);
|
|
@@ -220,14 +219,12 @@ function multiSplice(toSplice, splicingIndices) {
|
|
|
220
219
|
retainedSubstrings.push(
|
|
221
220
|
// splicingIndices[i] refers to an element between the first and last
|
|
222
221
|
// elements of the array, and will always be defined.
|
|
223
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
224
222
|
toSplice.substring(splicingIndices[i], splicingIndices[i + 1]));
|
|
225
223
|
}
|
|
226
224
|
}
|
|
227
225
|
// Get the last part to be included
|
|
228
226
|
retainedSubstrings.push(
|
|
229
227
|
// The last element of a non-empty array will always be defined.
|
|
230
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
231
228
|
toSplice.substring(splicingIndices[splicingIndices.length - 1]));
|
|
232
229
|
return retainedSubstrings.join('');
|
|
233
230
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remove-fenced-code.cjs","sourceRoot":"","sources":["../../src/transforms/remove-fenced-code.ts"],"names":[],"mappings":"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","sourcesContent":["import { hasProperty } from '@metamask/utils';\n\n/**\n * Two sets of feature labels, where:\n * - `active` is the set of labels that are active for the current build.\n * - `all` is the set of all labels that are declared in the codebase.\n *\n * For `ONLY_INCLUDE_IF` fences, the code fence removal transform will\n * include the fenced code if any of the specified labels are active. See\n * {@link removeFencedCode} for details.\n */\nexport type FeatureLabels = {\n active: ReadonlySet<string>;\n all: ReadonlySet<string>;\n};\n\nenum DirectiveTerminus {\n BEGIN = 'BEGIN',\n END = 'END',\n}\n\nexport enum DirectiveCommand {\n ONLY_INCLUDE_IF = 'ONLY_INCLUDE_IF',\n}\n\n// Matches lines starting with \"///:\", and any preceding whitespace, except\n// newlines. We except newlines to avoid eating blank lines preceding a fenced\n// line.\n// Double-negative RegEx credit: https://stackoverflow.com/a/3469155\nconst linesWithFenceRegex = /^[^\\S\\r\\n]*\\/\\/\\/:.*$/gmu;\n\n// Matches the first \"///:\" in a string, and any preceding whitespace\nconst fenceSentinelRegex = /^\\s*\\/\\/\\/:/u;\n\n// Breaks a fence directive into its constituent components.\n// At this stage of parsing, we are looking for one of:\n// - TERMINUS:COMMAND(PARAMS)\n// - TERMINUS:COMMAND\nconst directiveParsingRegex =\n /^([A-Z]+):([A-Z_]+)(?:\\(((?:\\w[-\\w]*,)*\\w[-\\w]*)\\))?$/u;\n\n/**\n * Removes fenced code from the given JavaScript source string. \"Fenced code\"\n * includes the entire fence lines, including their trailing newlines, and the\n * lines that they surround.\n *\n * A valid fence consists of two well-formed fence lines, separated by one or\n * more lines that should be excluded. The first line must contain a `BEGIN`\n * directive, and the second most contain an `END` directive. Both directives\n * must specify the same command.\n *\n * Here's an example of a valid fence:\n *\n * ```javascript\n * ///: BEGIN:ONLY_INCLUDE_IF(build-flask)\n * console.log('I am Flask.');\n * ///: END:ONLY_INCLUDE_IF\n * ```\n *\n * For details, please see the documentation.\n *\n * @param filePath - The path to the file being transformed.\n * @param fileContent - The contents of the file being transformed.\n * @param featureLabels - FeatureLabels that are currently active.\n * @returns A tuple of the post-transform file contents and a boolean indicating\n * whether they were modified.\n */\nexport function removeFencedCode(\n filePath: string,\n fileContent: string,\n featureLabels: FeatureLabels,\n): [string, boolean] {\n // Do not modify the file if we detect an inline sourcemap. For reasons\n // yet to be determined, the transform receives every file twice while in\n // watch mode, the second after Babel has transpiled the file. Babel adds\n // inline source maps to the file, something we will never do in our own\n // source files, so we use the existence of inline source maps to determine\n // whether we should ignore the file.\n if (/^\\/\\/# sourceMappingURL=/gmu.test(fileContent)) {\n return [fileContent, false];\n }\n\n // If we didn't match any lines, return the unmodified file contents.\n const matchedLines = [...fileContent.matchAll(linesWithFenceRegex)];\n\n if (matchedLines.length === 0) {\n return [fileContent, false];\n }\n\n // Parse fence lines\n const parsedDirectives = matchedLines.map((matchArray) => {\n const line = matchArray[0];\n\n /* istanbul ignore next: should be impossible */\n if (\n matchArray.index === undefined ||\n !line ||\n !fenceSentinelRegex.test(line)\n ) {\n throw new Error(\n getInvalidFenceLineMessage(\n filePath,\n line ?? '',\n `Fence sentinel may only appear at the start of a line, optionally preceded by whitespace.`,\n ),\n );\n }\n\n // Store the start and end indices of each line\n // Increment the end index by 1 to including the trailing newline when\n // performing string operations.\n const indices: [number, number] = [\n matchArray.index,\n matchArray.index + line.length + 1,\n ];\n\n const lineWithoutSentinel = line.replace(fenceSentinelRegex, '');\n if (!/^ \\w\\w+/u.test(lineWithoutSentinel)) {\n throw new Error(\n getInvalidFenceLineMessage(\n filePath,\n line,\n `Fence sentinel must be followed by a single space and an alphabetical string of two or more characters.`,\n ),\n );\n }\n\n const directiveMatches = lineWithoutSentinel\n .trim()\n .match(directiveParsingRegex);\n\n if (!directiveMatches) {\n throw new Error(\n getInvalidFenceLineMessage(\n filePath,\n line,\n `Failed to parse fence directive.`,\n ),\n );\n }\n\n // The first element of a RegEx match array is the input.\n // Typecast: If there's a match, the expected elements must exist.\n const [, terminus, command, parameters] = directiveMatches as [\n string,\n string,\n string,\n string,\n ];\n\n if (!isValidTerminus(terminus)) {\n throw new Error(\n getInvalidFenceLineMessage(\n filePath,\n line,\n `Line contains invalid directive terminus \"${terminus}\".`,\n ),\n );\n }\n\n if (!isValidCommand(command)) {\n throw new Error(\n getInvalidFenceLineMessage(\n filePath,\n line,\n `Line contains invalid directive command \"${command}\".`,\n ),\n );\n }\n\n if (terminus === DirectiveTerminus.BEGIN) {\n if (!parameters) {\n throw new Error(\n getInvalidParamsMessage(filePath, `No parameters specified.`),\n );\n }\n\n return {\n command,\n indices,\n line,\n parameters: parameters.split(','),\n terminus,\n };\n }\n return { command, indices, line, terminus };\n });\n\n if (parsedDirectives.length % 2 !== 0) {\n throw new Error(\n getInvalidFenceStructureMessage(\n filePath,\n `A valid fence consists of two fence lines, but the file contains an uneven number, \"${parsedDirectives.length}\", of fence lines.`,\n ),\n );\n }\n\n // The below for-loop iterates over the parsed fence directives and performs\n // the following work:\n // - Ensures that the array of parsed directives consists of valid directive\n // pairs, as specified in the documentation.\n // - For each directive pair, determines whether their fenced lines should be\n // removed for the current build, and if so, stores the indices we will use\n // to splice the file content string.\n\n const splicingIndices: number[] = [];\n let shouldSplice = false;\n let currentCommand: string;\n\n parsedDirectives.forEach((directive, i) => {\n const { line, indices, terminus, command } = directive;\n\n if (i % 2 === 0) {\n if (terminus !== DirectiveTerminus.BEGIN) {\n throw new Error(\n getInvalidFencePairMessage(\n filePath,\n line,\n `The first directive of a pair must be a \"BEGIN\" directive.`,\n ),\n );\n }\n\n const { parameters } = directive;\n currentCommand = command;\n validateCommand(command, parameters, filePath, featureLabels);\n\n const blockIsActive = parameters.some((param) =>\n featureLabels.active.has(param),\n );\n\n if (blockIsActive) {\n shouldSplice = false;\n } else {\n shouldSplice = true;\n\n // Add start index of BEGIN directive line to splicing indices\n splicingIndices.push(indices[0]);\n }\n } else {\n if (terminus !== DirectiveTerminus.END) {\n throw new Error(\n getInvalidFencePairMessage(\n filePath,\n line,\n `The second directive of a pair must be an \"END\" directive.`,\n ),\n );\n }\n\n /* istanbul ignore next: impossible until there's more than one command */\n if (command !== currentCommand) {\n throw new Error(\n getInvalidFencePairMessage(\n filePath,\n line,\n `Expected \"END\" directive to have command \"${currentCommand}\" but found \"${command}\".`,\n ),\n );\n }\n\n // Forbid empty fences\n const { line: previousLine, indices: previousIndices } =\n // We're only in this case if i > 0, so this will always be defined.\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n parsedDirectives[i - 1]!;\n if (fileContent.substring(previousIndices[1], indices[0]).trim() === '') {\n throw new Error(\n `Empty fence found in file \"${filePath}\":\\n${previousLine}\\n${line}\\n`,\n );\n }\n\n if (shouldSplice) {\n // Add end index of END directive line to splicing indices\n splicingIndices.push(indices[1]);\n }\n }\n });\n\n // This indicates that the present build type should include all fenced code,\n // and so we just returned the unmodified file contents.\n if (splicingIndices.length === 0) {\n return [fileContent, false];\n }\n\n /* istanbul ignore next: should be impossible */\n if (splicingIndices.length % 2 !== 0) {\n throw new Error(\n `Internal error while transforming file \"${filePath}\":\\nCollected an uneven number of splicing indices: \"${splicingIndices.length}\"`,\n );\n }\n\n return [multiSplice(fileContent, splicingIndices), true];\n}\n\n/**\n * Returns a copy of the given string, without the character ranges specified\n * by the splicing indices array.\n *\n * The splicing indices must be a non-empty, even-length array of non-negative\n * integers, specifying the character ranges to remove from the given string, as\n * follows:\n *\n * `[ start, end, start, end, start, end, ... ]`\n *\n * Throws if the array is not an even-length array of non-negative integers.\n *\n * @param toSplice - The string to splice.\n * @param splicingIndices - Indices to splice at.\n * @returns The spliced string.\n */\nexport function multiSplice(\n toSplice: string,\n splicingIndices: number[],\n): string {\n if (splicingIndices.length === 0 || splicingIndices.length % 2 !== 0) {\n throw new Error('Expected non-empty, even-length array.');\n }\n if (splicingIndices.some((index) => !Number.isInteger(index) || index < 0)) {\n throw new Error('Expected array of non-negative integers.');\n }\n\n const retainedSubstrings = [];\n\n // Get the first part to be included\n // The substring() call returns an empty string if splicingIndices[0] is 0,\n // which is exactly what we want in that case.\n retainedSubstrings.push(toSplice.substring(0, splicingIndices[0]));\n\n // This loop gets us all parts of the string that should be retained, except\n // the first and the last.\n // It iterates over all \"end\" indices of the array except the last one, and\n // pushes the substring between each \"end\" index and the next \"begin\" index\n // to the array of retained substrings.\n if (splicingIndices.length > 2) {\n // Note the boundary index of \"splicingIndices.length - 1\". This loop must\n // not iterate over the last element of the array, which is handled outside\n // of this loop.\n for (let i = 1; i < splicingIndices.length - 1; i += 2) {\n retainedSubstrings.push(\n // splicingIndices[i] refers to an element between the first and last\n // elements of the array, and will always be defined.\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n toSplice.substring(splicingIndices[i]!, splicingIndices[i + 1]),\n );\n }\n }\n\n // Get the last part to be included\n retainedSubstrings.push(\n // The last element of a non-empty array will always be defined.\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n toSplice.substring(splicingIndices[splicingIndices.length - 1]!),\n );\n return retainedSubstrings.join('');\n}\n\n/**\n * Gets an invalid fence line error message.\n *\n * @param filePath - The path to the file that caused the error.\n * @param line - The contents of the line with the error.\n * @param details - An explanation of the error.\n * @returns The error message.\n */\nfunction getInvalidFenceLineMessage(\n filePath: string,\n line: string,\n details: string,\n) {\n return `Invalid fence line in file \"${filePath}\": \"${line}\":\\n${details}`;\n}\n\n/**\n * Gets an invalid fence structure error message.\n *\n * @param filePath - The path to the file that caused the error.\n * @param details - An explanation of the error.\n * @returns The error message.\n */\nfunction getInvalidFenceStructureMessage(filePath: string, details: string) {\n return `Invalid fence structure in file \"${filePath}\":\\n${details}`;\n}\n\n/**\n * Gets an invalid fence pair error message.\n *\n * @param filePath - The path to the file that caused the error.\n * @param line - The contents of the line with the error.\n * @param details - An explanation of the error.\n * @returns The error message.\n */\nfunction getInvalidFencePairMessage(\n filePath: string,\n line: string,\n details: string,\n) {\n return `Invalid fence pair in file \"${filePath}\" due to line \"${line}\":\\n${details}`;\n}\n\n/**\n * Gets an invalid command params error message.\n *\n * @param filePath - The path to the file that caused the error.\n * @param details - An explanation of the error.\n * @param command - The command of the directive with the invalid parameters, if known.\n * @returns The error message.\n */\nfunction getInvalidParamsMessage(\n filePath: string,\n details: string,\n command?: string,\n) {\n return `Invalid code fence parameters in file \"${filePath}\"${\n command ? `for command \"${command}\"` : ''\n }:\\n${details}`;\n}\n\n/**\n * Checks whether the given terminus string is valid, i.e. one of `BEGIN` or `END`.\n *\n * @param terminus - The terminus string to validate.\n * @returns Whether the string is a valid terminus string.\n */\nfunction isValidTerminus(terminus: string): terminus is DirectiveTerminus {\n return hasProperty(DirectiveTerminus, terminus);\n}\n\n/**\n * Checks whether the given command string is valid.\n *\n * @param command - The command string to validate.\n * @returns Whether the string is a valid command string.\n */\nfunction isValidCommand(command: string): command is DirectiveCommand {\n return hasProperty(DirectiveCommand, command);\n}\n\n/**\n * Validates the specified command. Throws if validation fails.\n *\n * @param command - The command to validate.\n * @param params - The parameters of the command.\n * @param filePath - The path of the current file.\n * @param featureLabels - The possible feature labels.\n */\nexport function validateCommand(\n command: unknown,\n params: string[],\n filePath: string,\n featureLabels: FeatureLabels,\n): asserts command is DirectiveCommand {\n switch (command) {\n case DirectiveCommand.ONLY_INCLUDE_IF:\n if (!params || params.length === 0) {\n throw new Error(\n getInvalidParamsMessage(\n filePath,\n `No parameters specified.`,\n DirectiveCommand.ONLY_INCLUDE_IF,\n ),\n );\n }\n\n for (const param of params) {\n if (!featureLabels.all.has(param)) {\n throw new Error(\n getInvalidParamsMessage(\n filePath,\n `\"${param}\" is not a declared build feature.`,\n DirectiveCommand.ONLY_INCLUDE_IF,\n ),\n );\n }\n }\n break;\n\n default:\n throw new Error(`Unrecognized command \"${String(command)}\".`);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"remove-fenced-code.cjs","sourceRoot":"","sources":["../../src/transforms/remove-fenced-code.ts"],"names":[],"mappings":";;;AAAA,2CAA8C;AAgB9C,IAAK,iBAGJ;AAHD,WAAK,iBAAiB;IACpB,oCAAe,CAAA;IACf,gCAAW,CAAA;AACb,CAAC,EAHI,iBAAiB,KAAjB,iBAAiB,QAGrB;AAED,IAAY,gBAEX;AAFD,WAAY,gBAAgB;IAC1B,uDAAmC,CAAA;AACrC,CAAC,EAFW,gBAAgB,gCAAhB,gBAAgB,QAE3B;AAED,2EAA2E;AAC3E,8EAA8E;AAC9E,QAAQ;AACR,oEAAoE;AACpE,MAAM,mBAAmB,GAAG,0BAA0B,CAAC;AAEvD,qEAAqE;AACrE,MAAM,kBAAkB,GAAG,cAAc,CAAC;AAE1C,4DAA4D;AAC5D,uDAAuD;AACvD,6BAA6B;AAC7B,qBAAqB;AACrB,MAAM,qBAAqB,GACzB,wDAAwD,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,SAAgB,gBAAgB,CAC9B,QAAgB,EAChB,WAAmB,EACnB,aAA4B;IAE5B,uEAAuE;IACvE,yEAAyE;IACzE,yEAAyE;IACzE,wEAAwE;IACxE,2EAA2E;IAC3E,qCAAqC;IACrC,IAAI,6BAA6B,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;QACpD,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAC9B,CAAC;IAED,qEAAqE;IACrE,MAAM,YAAY,GAAG,CAAC,GAAG,WAAW,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAEpE,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAC9B,CAAC;IAED,oBAAoB;IACpB,MAAM,gBAAgB,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE;QACvD,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QAE3B,gDAAgD;QAChD,IACE,UAAU,CAAC,KAAK,KAAK,SAAS;YAC9B,CAAC,IAAI;YACL,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,EAC9B,CAAC;YACD,MAAM,IAAI,KAAK,CACb,0BAA0B,CACxB,QAAQ,EACR,IAAI,IAAI,EAAE,EACV,2FAA2F,CAC5F,CACF,CAAC;QACJ,CAAC;QAED,+CAA+C;QAC/C,sEAAsE;QACtE,gCAAgC;QAChC,MAAM,OAAO,GAAqB;YAChC,UAAU,CAAC,KAAK;YAChB,UAAU,CAAC,KAAK,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC;SACnC,CAAC;QAEF,MAAM,mBAAmB,GAAG,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;QACjE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,mBAAmB,CAAC,EAAE,CAAC;YAC1C,MAAM,IAAI,KAAK,CACb,0BAA0B,CACxB,QAAQ,EACR,IAAI,EACJ,yGAAyG,CAC1G,CACF,CAAC;QACJ,CAAC;QAED,MAAM,gBAAgB,GAAG,mBAAmB;aACzC,IAAI,EAAE;aACN,KAAK,CAAC,qBAAqB,CAAC,CAAC;QAEhC,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CACb,0BAA0B,CACxB,QAAQ,EACR,IAAI,EACJ,kCAAkC,CACnC,CACF,CAAC;QACJ,CAAC;QAED,yDAAyD;QACzD,kEAAkE;QAClE,MAAM,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAC,GAAG,gBAKzC,CAAC;QAEF,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CACb,0BAA0B,CACxB,QAAQ,EACR,IAAI,EACJ,6CAA6C,QAAQ,IAAI,CAC1D,CACF,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CACb,0BAA0B,CACxB,QAAQ,EACR,IAAI,EACJ,4CAA4C,OAAO,IAAI,CACxD,CACF,CAAC;QACJ,CAAC;QAED,IAAI,QAAQ,KAAK,iBAAiB,CAAC,KAAK,EAAE,CAAC;YACzC,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,MAAM,IAAI,KAAK,CACb,uBAAuB,CAAC,QAAQ,EAAE,0BAA0B,CAAC,CAC9D,CAAC;YACJ,CAAC;YAED,OAAO;gBACL,OAAO;gBACP,OAAO;gBACP,IAAI;gBACJ,UAAU,EAAE,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC;gBACjC,QAAQ;aACT,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CACb,+BAA+B,CAC7B,QAAQ,EACR,uFAAuF,gBAAgB,CAAC,MAAM,oBAAoB,CACnI,CACF,CAAC;IACJ,CAAC;IAED,4EAA4E;IAC5E,sBAAsB;IACtB,4EAA4E;IAC5E,8CAA8C;IAC9C,6EAA6E;IAC7E,6EAA6E;IAC7E,uCAAuC;IAEvC,MAAM,eAAe,GAAa,EAAE,CAAC;IACrC,IAAI,YAAY,GAAG,KAAK,CAAC;IACzB,IAAI,cAAsB,CAAC;IAE3B,gBAAgB,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE;QACxC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,SAAS,CAAC;QAEvD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAChB,IAAI,QAAQ,KAAK,iBAAiB,CAAC,KAAK,EAAE,CAAC;gBACzC,MAAM,IAAI,KAAK,CACb,0BAA0B,CACxB,QAAQ,EACR,IAAI,EACJ,4DAA4D,CAC7D,CACF,CAAC;YACJ,CAAC;YAED,MAAM,EAAE,UAAU,EAAE,GAAG,SAAS,CAAC;YACjC,cAAc,GAAG,OAAO,CAAC;YACzB,eAAe,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;YAE9D,MAAM,aAAa,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAC9C,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAChC,CAAC;YAEF,IAAI,aAAa,EAAE,CAAC;gBAClB,YAAY,GAAG,KAAK,CAAC;YACvB,CAAC;iBAAM,CAAC;gBACN,YAAY,GAAG,IAAI,CAAC;gBAEpB,8DAA8D;gBAC9D,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;aAAM,CAAC;YACN,IAAI,QAAQ,KAAK,iBAAiB,CAAC,GAAG,EAAE,CAAC;gBACvC,MAAM,IAAI,KAAK,CACb,0BAA0B,CACxB,QAAQ,EACR,IAAI,EACJ,4DAA4D,CAC7D,CACF,CAAC;YACJ,CAAC;YAED,0EAA0E;YAC1E,IAAI,OAAO,KAAK,cAAc,EAAE,CAAC;gBAC/B,MAAM,IAAI,KAAK,CACb,0BAA0B,CACxB,QAAQ,EACR,IAAI,EACJ,6CAA6C,cAAc,gBAAgB,OAAO,IAAI,CACvF,CACF,CAAC;YACJ,CAAC;YAED,sBAAsB;YACtB,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,eAAe,EAAE;YACpD,oEAAoE;YACpE,gBAAgB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC1B,IAAI,WAAW,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;gBACxE,MAAM,IAAI,KAAK,CACb,8BAA8B,QAAQ,OAAO,YAAY,KAAK,IAAI,IAAI,CACvE,CAAC;YACJ,CAAC;YAED,IAAI,YAAY,EAAE,CAAC;gBACjB,0DAA0D;gBAC1D,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,6EAA6E;IAC7E,wDAAwD;IACxD,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAC9B,CAAC;IAED,gDAAgD;IAChD,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CACb,2CAA2C,QAAQ,wDAAwD,eAAe,CAAC,MAAM,GAAG,CACrI,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,WAAW,CAAC,WAAW,EAAE,eAAe,CAAC,EAAE,IAAI,CAAC,CAAC;AAC3D,CAAC;AAjOD,4CAiOC;AAED;;;;;;;;;;;;;;;GAeG;AACH,SAAgB,WAAW,CACzB,QAAgB,EAChB,eAAyB;IAEzB,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACrE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC5D,CAAC;IACD,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC;QAC3E,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC9D,CAAC;IAED,MAAM,kBAAkB,GAAa,EAAE,CAAC;IAExC,oCAAoC;IACpC,2EAA2E;IAC3E,8CAA8C;IAC9C,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEnE,4EAA4E;IAC5E,0BAA0B;IAC1B,2EAA2E;IAC3E,2EAA2E;IAC3E,uCAAuC;IACvC,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,0EAA0E;QAC1E,2EAA2E;QAC3E,gBAAgB;QAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACvD,kBAAkB,CAAC,IAAI;YACrB,qEAAqE;YACrE,qDAAqD;YACrD,QAAQ,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAC/D,CAAC;QACJ,CAAC;IACH,CAAC;IAED,mCAAmC;IACnC,kBAAkB,CAAC,IAAI;IACrB,gEAAgE;IAChE,QAAQ,CAAC,SAAS,CAAC,eAAe,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAChE,CAAC;IACF,OAAO,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACrC,CAAC;AA1CD,kCA0CC;AAED;;;;;;;GAOG;AACH,SAAS,0BAA0B,CACjC,QAAgB,EAChB,IAAY,EACZ,OAAe;IAEf,OAAO,+BAA+B,QAAQ,OAAO,IAAI,OAAO,OAAO,EAAE,CAAC;AAC5E,CAAC;AAED;;;;;;GAMG;AACH,SAAS,+BAA+B,CAAC,QAAgB,EAAE,OAAe;IACxE,OAAO,oCAAoC,QAAQ,OAAO,OAAO,EAAE,CAAC;AACtE,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,0BAA0B,CACjC,QAAgB,EAChB,IAAY,EACZ,OAAe;IAEf,OAAO,+BAA+B,QAAQ,kBAAkB,IAAI,OAAO,OAAO,EAAE,CAAC;AACvF,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,uBAAuB,CAC9B,QAAgB,EAChB,OAAe,EACf,OAAgB;IAEhB,OAAO,0CAA0C,QAAQ,IACvD,OAAO,CAAC,CAAC,CAAC,gBAAgB,OAAO,GAAG,CAAC,CAAC,CAAC,EACzC,MAAM,OAAO,EAAE,CAAC;AAClB,CAAC;AAED;;;;;GAKG;AACH,SAAS,eAAe,CAAC,QAAgB;IACvC,OAAO,IAAA,mBAAW,EAAC,iBAAiB,EAAE,QAAQ,CAAC,CAAC;AAClD,CAAC;AAED;;;;;GAKG;AACH,SAAS,cAAc,CAAC,OAAe;IACrC,OAAO,IAAA,mBAAW,EAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;AAChD,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,eAAe,CAC7B,OAAgB,EAChB,MAAgB,EAChB,QAAgB,EAChB,aAA4B;IAE5B,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,gBAAgB,CAAC,eAAe;YACnC,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACnC,MAAM,IAAI,KAAK,CACb,uBAAuB,CACrB,QAAQ,EACR,0BAA0B,EAC1B,gBAAgB,CAAC,eAAe,CACjC,CACF,CAAC;YACJ,CAAC;YAED,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;oBAClC,MAAM,IAAI,KAAK,CACb,uBAAuB,CACrB,QAAQ,EACR,IAAI,KAAK,oCAAoC,EAC7C,gBAAgB,CAAC,eAAe,CACjC,CACF,CAAC;gBACJ,CAAC;YACH,CAAC;YACD,MAAM;QAER;YACE,MAAM,IAAI,KAAK,CAAC,yBAAyB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,CAAC;AACH,CAAC;AAlCD,0CAkCC","sourcesContent":["import { hasProperty } from '@metamask/utils';\n\n/**\n * Two sets of feature labels, where:\n * - `active` is the set of labels that are active for the current build.\n * - `all` is the set of all labels that are declared in the codebase.\n *\n * For `ONLY_INCLUDE_IF` fences, the code fence removal transform will\n * include the fenced code if any of the specified labels are active. See\n * {@link removeFencedCode} for details.\n */\nexport type FeatureLabels = {\n active: ReadonlySet<string>;\n all: ReadonlySet<string>;\n};\n\nenum DirectiveTerminus {\n BEGIN = 'BEGIN',\n END = 'END',\n}\n\nexport enum DirectiveCommand {\n ONLY_INCLUDE_IF = 'ONLY_INCLUDE_IF',\n}\n\n// Matches lines starting with \"///:\", and any preceding whitespace, except\n// newlines. We except newlines to avoid eating blank lines preceding a fenced\n// line.\n// Double-negative RegEx credit: https://stackoverflow.com/a/3469155\nconst linesWithFenceRegex = /^[^\\S\\r\\n]*\\/\\/\\/:.*$/gmu;\n\n// Matches the first \"///:\" in a string, and any preceding whitespace\nconst fenceSentinelRegex = /^\\s*\\/\\/\\/:/u;\n\n// Breaks a fence directive into its constituent components.\n// At this stage of parsing, we are looking for one of:\n// - TERMINUS:COMMAND(PARAMS)\n// - TERMINUS:COMMAND\nconst directiveParsingRegex =\n /^([A-Z]+):([A-Z_]+)(?:\\(((?:\\w[-\\w]*,)*\\w[-\\w]*)\\))?$/u;\n\n/**\n * Removes fenced code from the given JavaScript source string. \"Fenced code\"\n * includes the entire fence lines, including their trailing newlines, and the\n * lines that they surround.\n *\n * A valid fence consists of two well-formed fence lines, separated by one or\n * more lines that should be excluded. The first line must contain a `BEGIN`\n * directive, and the second most contain an `END` directive. Both directives\n * must specify the same command.\n *\n * Here's an example of a valid fence:\n *\n * ```javascript\n * ///: BEGIN:ONLY_INCLUDE_IF(build-flask)\n * console.log('I am Flask.');\n * ///: END:ONLY_INCLUDE_IF\n * ```\n *\n * For details, please see the documentation.\n *\n * @param filePath - The path to the file being transformed.\n * @param fileContent - The contents of the file being transformed.\n * @param featureLabels - FeatureLabels that are currently active.\n * @returns A tuple of the post-transform file contents and a boolean indicating\n * whether they were modified.\n */\nexport function removeFencedCode(\n filePath: string,\n fileContent: string,\n featureLabels: FeatureLabels,\n): [string, boolean] {\n // Do not modify the file if we detect an inline sourcemap. For reasons\n // yet to be determined, the transform receives every file twice while in\n // watch mode, the second after Babel has transpiled the file. Babel adds\n // inline source maps to the file, something we will never do in our own\n // source files, so we use the existence of inline source maps to determine\n // whether we should ignore the file.\n if (/^\\/\\/# sourceMappingURL=/gmu.test(fileContent)) {\n return [fileContent, false];\n }\n\n // If we didn't match any lines, return the unmodified file contents.\n const matchedLines = [...fileContent.matchAll(linesWithFenceRegex)];\n\n if (matchedLines.length === 0) {\n return [fileContent, false];\n }\n\n // Parse fence lines\n const parsedDirectives = matchedLines.map((matchArray) => {\n const line = matchArray[0];\n\n /* istanbul ignore next: should be impossible */\n if (\n matchArray.index === undefined ||\n !line ||\n !fenceSentinelRegex.test(line)\n ) {\n throw new Error(\n getInvalidFenceLineMessage(\n filePath,\n line ?? '',\n `Fence sentinel may only appear at the start of a line, optionally preceded by whitespace.`,\n ),\n );\n }\n\n // Store the start and end indices of each line\n // Increment the end index by 1 to including the trailing newline when\n // performing string operations.\n const indices: [number, number] = [\n matchArray.index,\n matchArray.index + line.length + 1,\n ];\n\n const lineWithoutSentinel = line.replace(fenceSentinelRegex, '');\n if (!/^ \\w\\w+/u.test(lineWithoutSentinel)) {\n throw new Error(\n getInvalidFenceLineMessage(\n filePath,\n line,\n `Fence sentinel must be followed by a single space and an alphabetical string of two or more characters.`,\n ),\n );\n }\n\n const directiveMatches = lineWithoutSentinel\n .trim()\n .match(directiveParsingRegex);\n\n if (!directiveMatches) {\n throw new Error(\n getInvalidFenceLineMessage(\n filePath,\n line,\n `Failed to parse fence directive.`,\n ),\n );\n }\n\n // The first element of a RegEx match array is the input.\n // Typecast: If there's a match, the expected elements must exist.\n const [, terminus, command, parameters] = directiveMatches as [\n string,\n string,\n string,\n string,\n ];\n\n if (!isValidTerminus(terminus)) {\n throw new Error(\n getInvalidFenceLineMessage(\n filePath,\n line,\n `Line contains invalid directive terminus \"${terminus}\".`,\n ),\n );\n }\n\n if (!isValidCommand(command)) {\n throw new Error(\n getInvalidFenceLineMessage(\n filePath,\n line,\n `Line contains invalid directive command \"${command}\".`,\n ),\n );\n }\n\n if (terminus === DirectiveTerminus.BEGIN) {\n if (!parameters) {\n throw new Error(\n getInvalidParamsMessage(filePath, `No parameters specified.`),\n );\n }\n\n return {\n command,\n indices,\n line,\n parameters: parameters.split(','),\n terminus,\n };\n }\n return { command, indices, line, terminus };\n });\n\n if (parsedDirectives.length % 2 !== 0) {\n throw new Error(\n getInvalidFenceStructureMessage(\n filePath,\n `A valid fence consists of two fence lines, but the file contains an uneven number, \"${parsedDirectives.length}\", of fence lines.`,\n ),\n );\n }\n\n // The below for-loop iterates over the parsed fence directives and performs\n // the following work:\n // - Ensures that the array of parsed directives consists of valid directive\n // pairs, as specified in the documentation.\n // - For each directive pair, determines whether their fenced lines should be\n // removed for the current build, and if so, stores the indices we will use\n // to splice the file content string.\n\n const splicingIndices: number[] = [];\n let shouldSplice = false;\n let currentCommand: string;\n\n parsedDirectives.forEach((directive, i) => {\n const { line, indices, terminus, command } = directive;\n\n if (i % 2 === 0) {\n if (terminus !== DirectiveTerminus.BEGIN) {\n throw new Error(\n getInvalidFencePairMessage(\n filePath,\n line,\n `The first directive of a pair must be a \"BEGIN\" directive.`,\n ),\n );\n }\n\n const { parameters } = directive;\n currentCommand = command;\n validateCommand(command, parameters, filePath, featureLabels);\n\n const blockIsActive = parameters.some((param) =>\n featureLabels.active.has(param),\n );\n\n if (blockIsActive) {\n shouldSplice = false;\n } else {\n shouldSplice = true;\n\n // Add start index of BEGIN directive line to splicing indices\n splicingIndices.push(indices[0]);\n }\n } else {\n if (terminus !== DirectiveTerminus.END) {\n throw new Error(\n getInvalidFencePairMessage(\n filePath,\n line,\n `The second directive of a pair must be an \"END\" directive.`,\n ),\n );\n }\n\n /* istanbul ignore next: impossible until there's more than one command */\n if (command !== currentCommand) {\n throw new Error(\n getInvalidFencePairMessage(\n filePath,\n line,\n `Expected \"END\" directive to have command \"${currentCommand}\" but found \"${command}\".`,\n ),\n );\n }\n\n // Forbid empty fences\n const { line: previousLine, indices: previousIndices } =\n // We're only in this case if i > 0, so this will always be defined.\n parsedDirectives[i - 1];\n if (fileContent.substring(previousIndices[1], indices[0]).trim() === '') {\n throw new Error(\n `Empty fence found in file \"${filePath}\":\\n${previousLine}\\n${line}\\n`,\n );\n }\n\n if (shouldSplice) {\n // Add end index of END directive line to splicing indices\n splicingIndices.push(indices[1]);\n }\n }\n });\n\n // This indicates that the present build type should include all fenced code,\n // and so we just returned the unmodified file contents.\n if (splicingIndices.length === 0) {\n return [fileContent, false];\n }\n\n /* istanbul ignore next: should be impossible */\n if (splicingIndices.length % 2 !== 0) {\n throw new Error(\n `Internal error while transforming file \"${filePath}\":\\nCollected an uneven number of splicing indices: \"${splicingIndices.length}\"`,\n );\n }\n\n return [multiSplice(fileContent, splicingIndices), true];\n}\n\n/**\n * Returns a copy of the given string, without the character ranges specified\n * by the splicing indices array.\n *\n * The splicing indices must be a non-empty, even-length array of non-negative\n * integers, specifying the character ranges to remove from the given string, as\n * follows:\n *\n * `[ start, end, start, end, start, end, ... ]`\n *\n * Throws if the array is not an even-length array of non-negative integers.\n *\n * @param toSplice - The string to splice.\n * @param splicingIndices - Indices to splice at.\n * @returns The spliced string.\n */\nexport function multiSplice(\n toSplice: string,\n splicingIndices: number[],\n): string {\n if (splicingIndices.length === 0 || splicingIndices.length % 2 !== 0) {\n throw new Error('Expected non-empty, even-length array.');\n }\n if (splicingIndices.some((index) => !Number.isInteger(index) || index < 0)) {\n throw new Error('Expected array of non-negative integers.');\n }\n\n const retainedSubstrings: string[] = [];\n\n // Get the first part to be included\n // The substring() call returns an empty string if splicingIndices[0] is 0,\n // which is exactly what we want in that case.\n retainedSubstrings.push(toSplice.substring(0, splicingIndices[0]));\n\n // This loop gets us all parts of the string that should be retained, except\n // the first and the last.\n // It iterates over all \"end\" indices of the array except the last one, and\n // pushes the substring between each \"end\" index and the next \"begin\" index\n // to the array of retained substrings.\n if (splicingIndices.length > 2) {\n // Note the boundary index of \"splicingIndices.length - 1\". This loop must\n // not iterate over the last element of the array, which is handled outside\n // of this loop.\n for (let i = 1; i < splicingIndices.length - 1; i += 2) {\n retainedSubstrings.push(\n // splicingIndices[i] refers to an element between the first and last\n // elements of the array, and will always be defined.\n toSplice.substring(splicingIndices[i], splicingIndices[i + 1]),\n );\n }\n }\n\n // Get the last part to be included\n retainedSubstrings.push(\n // The last element of a non-empty array will always be defined.\n toSplice.substring(splicingIndices[splicingIndices.length - 1]),\n );\n return retainedSubstrings.join('');\n}\n\n/**\n * Gets an invalid fence line error message.\n *\n * @param filePath - The path to the file that caused the error.\n * @param line - The contents of the line with the error.\n * @param details - An explanation of the error.\n * @returns The error message.\n */\nfunction getInvalidFenceLineMessage(\n filePath: string,\n line: string,\n details: string,\n) {\n return `Invalid fence line in file \"${filePath}\": \"${line}\":\\n${details}`;\n}\n\n/**\n * Gets an invalid fence structure error message.\n *\n * @param filePath - The path to the file that caused the error.\n * @param details - An explanation of the error.\n * @returns The error message.\n */\nfunction getInvalidFenceStructureMessage(filePath: string, details: string) {\n return `Invalid fence structure in file \"${filePath}\":\\n${details}`;\n}\n\n/**\n * Gets an invalid fence pair error message.\n *\n * @param filePath - The path to the file that caused the error.\n * @param line - The contents of the line with the error.\n * @param details - An explanation of the error.\n * @returns The error message.\n */\nfunction getInvalidFencePairMessage(\n filePath: string,\n line: string,\n details: string,\n) {\n return `Invalid fence pair in file \"${filePath}\" due to line \"${line}\":\\n${details}`;\n}\n\n/**\n * Gets an invalid command params error message.\n *\n * @param filePath - The path to the file that caused the error.\n * @param details - An explanation of the error.\n * @param command - The command of the directive with the invalid parameters, if known.\n * @returns The error message.\n */\nfunction getInvalidParamsMessage(\n filePath: string,\n details: string,\n command?: string,\n) {\n return `Invalid code fence parameters in file \"${filePath}\"${\n command ? `for command \"${command}\"` : ''\n }:\\n${details}`;\n}\n\n/**\n * Checks whether the given terminus string is valid, i.e. one of `BEGIN` or `END`.\n *\n * @param terminus - The terminus string to validate.\n * @returns Whether the string is a valid terminus string.\n */\nfunction isValidTerminus(terminus: string): terminus is DirectiveTerminus {\n return hasProperty(DirectiveTerminus, terminus);\n}\n\n/**\n * Checks whether the given command string is valid.\n *\n * @param command - The command string to validate.\n * @returns Whether the string is a valid command string.\n */\nfunction isValidCommand(command: string): command is DirectiveCommand {\n return hasProperty(DirectiveCommand, command);\n}\n\n/**\n * Validates the specified command. Throws if validation fails.\n *\n * @param command - The command to validate.\n * @param params - The parameters of the command.\n * @param filePath - The path of the current file.\n * @param featureLabels - The possible feature labels.\n */\nexport function validateCommand(\n command: unknown,\n params: string[],\n filePath: string,\n featureLabels: FeatureLabels,\n): asserts command is DirectiveCommand {\n switch (command) {\n case DirectiveCommand.ONLY_INCLUDE_IF:\n if (!params || params.length === 0) {\n throw new Error(\n getInvalidParamsMessage(\n filePath,\n `No parameters specified.`,\n DirectiveCommand.ONLY_INCLUDE_IF,\n ),\n );\n }\n\n for (const param of params) {\n if (!featureLabels.all.has(param)) {\n throw new Error(\n getInvalidParamsMessage(\n filePath,\n `\"${param}\" is not a declared build feature.`,\n DirectiveCommand.ONLY_INCLUDE_IF,\n ),\n );\n }\n }\n break;\n\n default:\n throw new Error(`Unrecognized command \"${String(command)}\".`);\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remove-fenced-code.d.cts","sourceRoot":"","sources":["../../src/transforms/remove-fenced-code.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5B,GAAG,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC1B,CAAC;AAOF,oBAAY,gBAAgB;IAC1B,eAAe,oBAAoB;CACpC;AAkBD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,aAAa,GAC3B,CAAC,MAAM,EAAE,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"remove-fenced-code.d.cts","sourceRoot":"","sources":["../../src/transforms/remove-fenced-code.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5B,GAAG,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC1B,CAAC;AAOF,oBAAY,gBAAgB;IAC1B,eAAe,oBAAoB;CACpC;AAkBD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,aAAa,GAC3B,CAAC,MAAM,EAAE,OAAO,CAAC,CA6NnB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,MAAM,EAAE,GACxB,MAAM,CAuCR;AAmFD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,EAAE,EAChB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,aAAa,GAC3B,OAAO,CAAC,OAAO,IAAI,gBAAgB,CA6BrC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remove-fenced-code.d.mts","sourceRoot":"","sources":["../../src/transforms/remove-fenced-code.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5B,GAAG,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC1B,CAAC;AAOF,oBAAY,gBAAgB;IAC1B,eAAe,oBAAoB;CACpC;AAkBD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,aAAa,GAC3B,CAAC,MAAM,EAAE,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"remove-fenced-code.d.mts","sourceRoot":"","sources":["../../src/transforms/remove-fenced-code.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5B,GAAG,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC1B,CAAC;AAOF,oBAAY,gBAAgB;IAC1B,eAAe,oBAAoB;CACpC;AAkBD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,aAAa,GAC3B,CAAC,MAAM,EAAE,OAAO,CAAC,CA6NnB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,MAAM,EAAE,GACxB,MAAM,CAuCR;AAmFD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,EAAE,EAChB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,aAAa,GAC3B,OAAO,CAAC,OAAO,IAAI,gBAAgB,CA6BrC"}
|
|
@@ -153,7 +153,6 @@ export function removeFencedCode(filePath, fileContent, featureLabels) {
|
|
|
153
153
|
// Forbid empty fences
|
|
154
154
|
const { line: previousLine, indices: previousIndices } =
|
|
155
155
|
// We're only in this case if i > 0, so this will always be defined.
|
|
156
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
157
156
|
parsedDirectives[i - 1];
|
|
158
157
|
if (fileContent.substring(previousIndices[1], indices[0]).trim() === '') {
|
|
159
158
|
throw new Error(`Empty fence found in file "${filePath}":\n${previousLine}\n${line}\n`);
|
|
@@ -216,14 +215,12 @@ export function multiSplice(toSplice, splicingIndices) {
|
|
|
216
215
|
retainedSubstrings.push(
|
|
217
216
|
// splicingIndices[i] refers to an element between the first and last
|
|
218
217
|
// elements of the array, and will always be defined.
|
|
219
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
220
218
|
toSplice.substring(splicingIndices[i], splicingIndices[i + 1]));
|
|
221
219
|
}
|
|
222
220
|
}
|
|
223
221
|
// Get the last part to be included
|
|
224
222
|
retainedSubstrings.push(
|
|
225
223
|
// The last element of a non-empty array will always be defined.
|
|
226
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
227
224
|
toSplice.substring(splicingIndices[splicingIndices.length - 1]));
|
|
228
225
|
return retainedSubstrings.join('');
|
|
229
226
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remove-fenced-code.mjs","sourceRoot":"","sources":["../../src/transforms/remove-fenced-code.ts"],"names":[],"mappings":"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","sourcesContent":["import { hasProperty } from '@metamask/utils';\n\n/**\n * Two sets of feature labels, where:\n * - `active` is the set of labels that are active for the current build.\n * - `all` is the set of all labels that are declared in the codebase.\n *\n * For `ONLY_INCLUDE_IF` fences, the code fence removal transform will\n * include the fenced code if any of the specified labels are active. See\n * {@link removeFencedCode} for details.\n */\nexport type FeatureLabels = {\n active: ReadonlySet<string>;\n all: ReadonlySet<string>;\n};\n\nenum DirectiveTerminus {\n BEGIN = 'BEGIN',\n END = 'END',\n}\n\nexport enum DirectiveCommand {\n ONLY_INCLUDE_IF = 'ONLY_INCLUDE_IF',\n}\n\n// Matches lines starting with \"///:\", and any preceding whitespace, except\n// newlines. We except newlines to avoid eating blank lines preceding a fenced\n// line.\n// Double-negative RegEx credit: https://stackoverflow.com/a/3469155\nconst linesWithFenceRegex = /^[^\\S\\r\\n]*\\/\\/\\/:.*$/gmu;\n\n// Matches the first \"///:\" in a string, and any preceding whitespace\nconst fenceSentinelRegex = /^\\s*\\/\\/\\/:/u;\n\n// Breaks a fence directive into its constituent components.\n// At this stage of parsing, we are looking for one of:\n// - TERMINUS:COMMAND(PARAMS)\n// - TERMINUS:COMMAND\nconst directiveParsingRegex =\n /^([A-Z]+):([A-Z_]+)(?:\\(((?:\\w[-\\w]*,)*\\w[-\\w]*)\\))?$/u;\n\n/**\n * Removes fenced code from the given JavaScript source string. \"Fenced code\"\n * includes the entire fence lines, including their trailing newlines, and the\n * lines that they surround.\n *\n * A valid fence consists of two well-formed fence lines, separated by one or\n * more lines that should be excluded. The first line must contain a `BEGIN`\n * directive, and the second most contain an `END` directive. Both directives\n * must specify the same command.\n *\n * Here's an example of a valid fence:\n *\n * ```javascript\n * ///: BEGIN:ONLY_INCLUDE_IF(build-flask)\n * console.log('I am Flask.');\n * ///: END:ONLY_INCLUDE_IF\n * ```\n *\n * For details, please see the documentation.\n *\n * @param filePath - The path to the file being transformed.\n * @param fileContent - The contents of the file being transformed.\n * @param featureLabels - FeatureLabels that are currently active.\n * @returns A tuple of the post-transform file contents and a boolean indicating\n * whether they were modified.\n */\nexport function removeFencedCode(\n filePath: string,\n fileContent: string,\n featureLabels: FeatureLabels,\n): [string, boolean] {\n // Do not modify the file if we detect an inline sourcemap. For reasons\n // yet to be determined, the transform receives every file twice while in\n // watch mode, the second after Babel has transpiled the file. Babel adds\n // inline source maps to the file, something we will never do in our own\n // source files, so we use the existence of inline source maps to determine\n // whether we should ignore the file.\n if (/^\\/\\/# sourceMappingURL=/gmu.test(fileContent)) {\n return [fileContent, false];\n }\n\n // If we didn't match any lines, return the unmodified file contents.\n const matchedLines = [...fileContent.matchAll(linesWithFenceRegex)];\n\n if (matchedLines.length === 0) {\n return [fileContent, false];\n }\n\n // Parse fence lines\n const parsedDirectives = matchedLines.map((matchArray) => {\n const line = matchArray[0];\n\n /* istanbul ignore next: should be impossible */\n if (\n matchArray.index === undefined ||\n !line ||\n !fenceSentinelRegex.test(line)\n ) {\n throw new Error(\n getInvalidFenceLineMessage(\n filePath,\n line ?? '',\n `Fence sentinel may only appear at the start of a line, optionally preceded by whitespace.`,\n ),\n );\n }\n\n // Store the start and end indices of each line\n // Increment the end index by 1 to including the trailing newline when\n // performing string operations.\n const indices: [number, number] = [\n matchArray.index,\n matchArray.index + line.length + 1,\n ];\n\n const lineWithoutSentinel = line.replace(fenceSentinelRegex, '');\n if (!/^ \\w\\w+/u.test(lineWithoutSentinel)) {\n throw new Error(\n getInvalidFenceLineMessage(\n filePath,\n line,\n `Fence sentinel must be followed by a single space and an alphabetical string of two or more characters.`,\n ),\n );\n }\n\n const directiveMatches = lineWithoutSentinel\n .trim()\n .match(directiveParsingRegex);\n\n if (!directiveMatches) {\n throw new Error(\n getInvalidFenceLineMessage(\n filePath,\n line,\n `Failed to parse fence directive.`,\n ),\n );\n }\n\n // The first element of a RegEx match array is the input.\n // Typecast: If there's a match, the expected elements must exist.\n const [, terminus, command, parameters] = directiveMatches as [\n string,\n string,\n string,\n string,\n ];\n\n if (!isValidTerminus(terminus)) {\n throw new Error(\n getInvalidFenceLineMessage(\n filePath,\n line,\n `Line contains invalid directive terminus \"${terminus}\".`,\n ),\n );\n }\n\n if (!isValidCommand(command)) {\n throw new Error(\n getInvalidFenceLineMessage(\n filePath,\n line,\n `Line contains invalid directive command \"${command}\".`,\n ),\n );\n }\n\n if (terminus === DirectiveTerminus.BEGIN) {\n if (!parameters) {\n throw new Error(\n getInvalidParamsMessage(filePath, `No parameters specified.`),\n );\n }\n\n return {\n command,\n indices,\n line,\n parameters: parameters.split(','),\n terminus,\n };\n }\n return { command, indices, line, terminus };\n });\n\n if (parsedDirectives.length % 2 !== 0) {\n throw new Error(\n getInvalidFenceStructureMessage(\n filePath,\n `A valid fence consists of two fence lines, but the file contains an uneven number, \"${parsedDirectives.length}\", of fence lines.`,\n ),\n );\n }\n\n // The below for-loop iterates over the parsed fence directives and performs\n // the following work:\n // - Ensures that the array of parsed directives consists of valid directive\n // pairs, as specified in the documentation.\n // - For each directive pair, determines whether their fenced lines should be\n // removed for the current build, and if so, stores the indices we will use\n // to splice the file content string.\n\n const splicingIndices: number[] = [];\n let shouldSplice = false;\n let currentCommand: string;\n\n parsedDirectives.forEach((directive, i) => {\n const { line, indices, terminus, command } = directive;\n\n if (i % 2 === 0) {\n if (terminus !== DirectiveTerminus.BEGIN) {\n throw new Error(\n getInvalidFencePairMessage(\n filePath,\n line,\n `The first directive of a pair must be a \"BEGIN\" directive.`,\n ),\n );\n }\n\n const { parameters } = directive;\n currentCommand = command;\n validateCommand(command, parameters, filePath, featureLabels);\n\n const blockIsActive = parameters.some((param) =>\n featureLabels.active.has(param),\n );\n\n if (blockIsActive) {\n shouldSplice = false;\n } else {\n shouldSplice = true;\n\n // Add start index of BEGIN directive line to splicing indices\n splicingIndices.push(indices[0]);\n }\n } else {\n if (terminus !== DirectiveTerminus.END) {\n throw new Error(\n getInvalidFencePairMessage(\n filePath,\n line,\n `The second directive of a pair must be an \"END\" directive.`,\n ),\n );\n }\n\n /* istanbul ignore next: impossible until there's more than one command */\n if (command !== currentCommand) {\n throw new Error(\n getInvalidFencePairMessage(\n filePath,\n line,\n `Expected \"END\" directive to have command \"${currentCommand}\" but found \"${command}\".`,\n ),\n );\n }\n\n // Forbid empty fences\n const { line: previousLine, indices: previousIndices } =\n // We're only in this case if i > 0, so this will always be defined.\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n parsedDirectives[i - 1]!;\n if (fileContent.substring(previousIndices[1], indices[0]).trim() === '') {\n throw new Error(\n `Empty fence found in file \"${filePath}\":\\n${previousLine}\\n${line}\\n`,\n );\n }\n\n if (shouldSplice) {\n // Add end index of END directive line to splicing indices\n splicingIndices.push(indices[1]);\n }\n }\n });\n\n // This indicates that the present build type should include all fenced code,\n // and so we just returned the unmodified file contents.\n if (splicingIndices.length === 0) {\n return [fileContent, false];\n }\n\n /* istanbul ignore next: should be impossible */\n if (splicingIndices.length % 2 !== 0) {\n throw new Error(\n `Internal error while transforming file \"${filePath}\":\\nCollected an uneven number of splicing indices: \"${splicingIndices.length}\"`,\n );\n }\n\n return [multiSplice(fileContent, splicingIndices), true];\n}\n\n/**\n * Returns a copy of the given string, without the character ranges specified\n * by the splicing indices array.\n *\n * The splicing indices must be a non-empty, even-length array of non-negative\n * integers, specifying the character ranges to remove from the given string, as\n * follows:\n *\n * `[ start, end, start, end, start, end, ... ]`\n *\n * Throws if the array is not an even-length array of non-negative integers.\n *\n * @param toSplice - The string to splice.\n * @param splicingIndices - Indices to splice at.\n * @returns The spliced string.\n */\nexport function multiSplice(\n toSplice: string,\n splicingIndices: number[],\n): string {\n if (splicingIndices.length === 0 || splicingIndices.length % 2 !== 0) {\n throw new Error('Expected non-empty, even-length array.');\n }\n if (splicingIndices.some((index) => !Number.isInteger(index) || index < 0)) {\n throw new Error('Expected array of non-negative integers.');\n }\n\n const retainedSubstrings = [];\n\n // Get the first part to be included\n // The substring() call returns an empty string if splicingIndices[0] is 0,\n // which is exactly what we want in that case.\n retainedSubstrings.push(toSplice.substring(0, splicingIndices[0]));\n\n // This loop gets us all parts of the string that should be retained, except\n // the first and the last.\n // It iterates over all \"end\" indices of the array except the last one, and\n // pushes the substring between each \"end\" index and the next \"begin\" index\n // to the array of retained substrings.\n if (splicingIndices.length > 2) {\n // Note the boundary index of \"splicingIndices.length - 1\". This loop must\n // not iterate over the last element of the array, which is handled outside\n // of this loop.\n for (let i = 1; i < splicingIndices.length - 1; i += 2) {\n retainedSubstrings.push(\n // splicingIndices[i] refers to an element between the first and last\n // elements of the array, and will always be defined.\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n toSplice.substring(splicingIndices[i]!, splicingIndices[i + 1]),\n );\n }\n }\n\n // Get the last part to be included\n retainedSubstrings.push(\n // The last element of a non-empty array will always be defined.\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n toSplice.substring(splicingIndices[splicingIndices.length - 1]!),\n );\n return retainedSubstrings.join('');\n}\n\n/**\n * Gets an invalid fence line error message.\n *\n * @param filePath - The path to the file that caused the error.\n * @param line - The contents of the line with the error.\n * @param details - An explanation of the error.\n * @returns The error message.\n */\nfunction getInvalidFenceLineMessage(\n filePath: string,\n line: string,\n details: string,\n) {\n return `Invalid fence line in file \"${filePath}\": \"${line}\":\\n${details}`;\n}\n\n/**\n * Gets an invalid fence structure error message.\n *\n * @param filePath - The path to the file that caused the error.\n * @param details - An explanation of the error.\n * @returns The error message.\n */\nfunction getInvalidFenceStructureMessage(filePath: string, details: string) {\n return `Invalid fence structure in file \"${filePath}\":\\n${details}`;\n}\n\n/**\n * Gets an invalid fence pair error message.\n *\n * @param filePath - The path to the file that caused the error.\n * @param line - The contents of the line with the error.\n * @param details - An explanation of the error.\n * @returns The error message.\n */\nfunction getInvalidFencePairMessage(\n filePath: string,\n line: string,\n details: string,\n) {\n return `Invalid fence pair in file \"${filePath}\" due to line \"${line}\":\\n${details}`;\n}\n\n/**\n * Gets an invalid command params error message.\n *\n * @param filePath - The path to the file that caused the error.\n * @param details - An explanation of the error.\n * @param command - The command of the directive with the invalid parameters, if known.\n * @returns The error message.\n */\nfunction getInvalidParamsMessage(\n filePath: string,\n details: string,\n command?: string,\n) {\n return `Invalid code fence parameters in file \"${filePath}\"${\n command ? `for command \"${command}\"` : ''\n }:\\n${details}`;\n}\n\n/**\n * Checks whether the given terminus string is valid, i.e. one of `BEGIN` or `END`.\n *\n * @param terminus - The terminus string to validate.\n * @returns Whether the string is a valid terminus string.\n */\nfunction isValidTerminus(terminus: string): terminus is DirectiveTerminus {\n return hasProperty(DirectiveTerminus, terminus);\n}\n\n/**\n * Checks whether the given command string is valid.\n *\n * @param command - The command string to validate.\n * @returns Whether the string is a valid command string.\n */\nfunction isValidCommand(command: string): command is DirectiveCommand {\n return hasProperty(DirectiveCommand, command);\n}\n\n/**\n * Validates the specified command. Throws if validation fails.\n *\n * @param command - The command to validate.\n * @param params - The parameters of the command.\n * @param filePath - The path of the current file.\n * @param featureLabels - The possible feature labels.\n */\nexport function validateCommand(\n command: unknown,\n params: string[],\n filePath: string,\n featureLabels: FeatureLabels,\n): asserts command is DirectiveCommand {\n switch (command) {\n case DirectiveCommand.ONLY_INCLUDE_IF:\n if (!params || params.length === 0) {\n throw new Error(\n getInvalidParamsMessage(\n filePath,\n `No parameters specified.`,\n DirectiveCommand.ONLY_INCLUDE_IF,\n ),\n );\n }\n\n for (const param of params) {\n if (!featureLabels.all.has(param)) {\n throw new Error(\n getInvalidParamsMessage(\n filePath,\n `\"${param}\" is not a declared build feature.`,\n DirectiveCommand.ONLY_INCLUDE_IF,\n ),\n );\n }\n }\n break;\n\n default:\n throw new Error(`Unrecognized command \"${String(command)}\".`);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"remove-fenced-code.mjs","sourceRoot":"","sources":["../../src/transforms/remove-fenced-code.ts"],"names":[],"mappings":"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","sourcesContent":["import { hasProperty } from '@metamask/utils';\n\n/**\n * Two sets of feature labels, where:\n * - `active` is the set of labels that are active for the current build.\n * - `all` is the set of all labels that are declared in the codebase.\n *\n * For `ONLY_INCLUDE_IF` fences, the code fence removal transform will\n * include the fenced code if any of the specified labels are active. See\n * {@link removeFencedCode} for details.\n */\nexport type FeatureLabels = {\n active: ReadonlySet<string>;\n all: ReadonlySet<string>;\n};\n\nenum DirectiveTerminus {\n BEGIN = 'BEGIN',\n END = 'END',\n}\n\nexport enum DirectiveCommand {\n ONLY_INCLUDE_IF = 'ONLY_INCLUDE_IF',\n}\n\n// Matches lines starting with \"///:\", and any preceding whitespace, except\n// newlines. We except newlines to avoid eating blank lines preceding a fenced\n// line.\n// Double-negative RegEx credit: https://stackoverflow.com/a/3469155\nconst linesWithFenceRegex = /^[^\\S\\r\\n]*\\/\\/\\/:.*$/gmu;\n\n// Matches the first \"///:\" in a string, and any preceding whitespace\nconst fenceSentinelRegex = /^\\s*\\/\\/\\/:/u;\n\n// Breaks a fence directive into its constituent components.\n// At this stage of parsing, we are looking for one of:\n// - TERMINUS:COMMAND(PARAMS)\n// - TERMINUS:COMMAND\nconst directiveParsingRegex =\n /^([A-Z]+):([A-Z_]+)(?:\\(((?:\\w[-\\w]*,)*\\w[-\\w]*)\\))?$/u;\n\n/**\n * Removes fenced code from the given JavaScript source string. \"Fenced code\"\n * includes the entire fence lines, including their trailing newlines, and the\n * lines that they surround.\n *\n * A valid fence consists of two well-formed fence lines, separated by one or\n * more lines that should be excluded. The first line must contain a `BEGIN`\n * directive, and the second most contain an `END` directive. Both directives\n * must specify the same command.\n *\n * Here's an example of a valid fence:\n *\n * ```javascript\n * ///: BEGIN:ONLY_INCLUDE_IF(build-flask)\n * console.log('I am Flask.');\n * ///: END:ONLY_INCLUDE_IF\n * ```\n *\n * For details, please see the documentation.\n *\n * @param filePath - The path to the file being transformed.\n * @param fileContent - The contents of the file being transformed.\n * @param featureLabels - FeatureLabels that are currently active.\n * @returns A tuple of the post-transform file contents and a boolean indicating\n * whether they were modified.\n */\nexport function removeFencedCode(\n filePath: string,\n fileContent: string,\n featureLabels: FeatureLabels,\n): [string, boolean] {\n // Do not modify the file if we detect an inline sourcemap. For reasons\n // yet to be determined, the transform receives every file twice while in\n // watch mode, the second after Babel has transpiled the file. Babel adds\n // inline source maps to the file, something we will never do in our own\n // source files, so we use the existence of inline source maps to determine\n // whether we should ignore the file.\n if (/^\\/\\/# sourceMappingURL=/gmu.test(fileContent)) {\n return [fileContent, false];\n }\n\n // If we didn't match any lines, return the unmodified file contents.\n const matchedLines = [...fileContent.matchAll(linesWithFenceRegex)];\n\n if (matchedLines.length === 0) {\n return [fileContent, false];\n }\n\n // Parse fence lines\n const parsedDirectives = matchedLines.map((matchArray) => {\n const line = matchArray[0];\n\n /* istanbul ignore next: should be impossible */\n if (\n matchArray.index === undefined ||\n !line ||\n !fenceSentinelRegex.test(line)\n ) {\n throw new Error(\n getInvalidFenceLineMessage(\n filePath,\n line ?? '',\n `Fence sentinel may only appear at the start of a line, optionally preceded by whitespace.`,\n ),\n );\n }\n\n // Store the start and end indices of each line\n // Increment the end index by 1 to including the trailing newline when\n // performing string operations.\n const indices: [number, number] = [\n matchArray.index,\n matchArray.index + line.length + 1,\n ];\n\n const lineWithoutSentinel = line.replace(fenceSentinelRegex, '');\n if (!/^ \\w\\w+/u.test(lineWithoutSentinel)) {\n throw new Error(\n getInvalidFenceLineMessage(\n filePath,\n line,\n `Fence sentinel must be followed by a single space and an alphabetical string of two or more characters.`,\n ),\n );\n }\n\n const directiveMatches = lineWithoutSentinel\n .trim()\n .match(directiveParsingRegex);\n\n if (!directiveMatches) {\n throw new Error(\n getInvalidFenceLineMessage(\n filePath,\n line,\n `Failed to parse fence directive.`,\n ),\n );\n }\n\n // The first element of a RegEx match array is the input.\n // Typecast: If there's a match, the expected elements must exist.\n const [, terminus, command, parameters] = directiveMatches as [\n string,\n string,\n string,\n string,\n ];\n\n if (!isValidTerminus(terminus)) {\n throw new Error(\n getInvalidFenceLineMessage(\n filePath,\n line,\n `Line contains invalid directive terminus \"${terminus}\".`,\n ),\n );\n }\n\n if (!isValidCommand(command)) {\n throw new Error(\n getInvalidFenceLineMessage(\n filePath,\n line,\n `Line contains invalid directive command \"${command}\".`,\n ),\n );\n }\n\n if (terminus === DirectiveTerminus.BEGIN) {\n if (!parameters) {\n throw new Error(\n getInvalidParamsMessage(filePath, `No parameters specified.`),\n );\n }\n\n return {\n command,\n indices,\n line,\n parameters: parameters.split(','),\n terminus,\n };\n }\n return { command, indices, line, terminus };\n });\n\n if (parsedDirectives.length % 2 !== 0) {\n throw new Error(\n getInvalidFenceStructureMessage(\n filePath,\n `A valid fence consists of two fence lines, but the file contains an uneven number, \"${parsedDirectives.length}\", of fence lines.`,\n ),\n );\n }\n\n // The below for-loop iterates over the parsed fence directives and performs\n // the following work:\n // - Ensures that the array of parsed directives consists of valid directive\n // pairs, as specified in the documentation.\n // - For each directive pair, determines whether their fenced lines should be\n // removed for the current build, and if so, stores the indices we will use\n // to splice the file content string.\n\n const splicingIndices: number[] = [];\n let shouldSplice = false;\n let currentCommand: string;\n\n parsedDirectives.forEach((directive, i) => {\n const { line, indices, terminus, command } = directive;\n\n if (i % 2 === 0) {\n if (terminus !== DirectiveTerminus.BEGIN) {\n throw new Error(\n getInvalidFencePairMessage(\n filePath,\n line,\n `The first directive of a pair must be a \"BEGIN\" directive.`,\n ),\n );\n }\n\n const { parameters } = directive;\n currentCommand = command;\n validateCommand(command, parameters, filePath, featureLabels);\n\n const blockIsActive = parameters.some((param) =>\n featureLabels.active.has(param),\n );\n\n if (blockIsActive) {\n shouldSplice = false;\n } else {\n shouldSplice = true;\n\n // Add start index of BEGIN directive line to splicing indices\n splicingIndices.push(indices[0]);\n }\n } else {\n if (terminus !== DirectiveTerminus.END) {\n throw new Error(\n getInvalidFencePairMessage(\n filePath,\n line,\n `The second directive of a pair must be an \"END\" directive.`,\n ),\n );\n }\n\n /* istanbul ignore next: impossible until there's more than one command */\n if (command !== currentCommand) {\n throw new Error(\n getInvalidFencePairMessage(\n filePath,\n line,\n `Expected \"END\" directive to have command \"${currentCommand}\" but found \"${command}\".`,\n ),\n );\n }\n\n // Forbid empty fences\n const { line: previousLine, indices: previousIndices } =\n // We're only in this case if i > 0, so this will always be defined.\n parsedDirectives[i - 1];\n if (fileContent.substring(previousIndices[1], indices[0]).trim() === '') {\n throw new Error(\n `Empty fence found in file \"${filePath}\":\\n${previousLine}\\n${line}\\n`,\n );\n }\n\n if (shouldSplice) {\n // Add end index of END directive line to splicing indices\n splicingIndices.push(indices[1]);\n }\n }\n });\n\n // This indicates that the present build type should include all fenced code,\n // and so we just returned the unmodified file contents.\n if (splicingIndices.length === 0) {\n return [fileContent, false];\n }\n\n /* istanbul ignore next: should be impossible */\n if (splicingIndices.length % 2 !== 0) {\n throw new Error(\n `Internal error while transforming file \"${filePath}\":\\nCollected an uneven number of splicing indices: \"${splicingIndices.length}\"`,\n );\n }\n\n return [multiSplice(fileContent, splicingIndices), true];\n}\n\n/**\n * Returns a copy of the given string, without the character ranges specified\n * by the splicing indices array.\n *\n * The splicing indices must be a non-empty, even-length array of non-negative\n * integers, specifying the character ranges to remove from the given string, as\n * follows:\n *\n * `[ start, end, start, end, start, end, ... ]`\n *\n * Throws if the array is not an even-length array of non-negative integers.\n *\n * @param toSplice - The string to splice.\n * @param splicingIndices - Indices to splice at.\n * @returns The spliced string.\n */\nexport function multiSplice(\n toSplice: string,\n splicingIndices: number[],\n): string {\n if (splicingIndices.length === 0 || splicingIndices.length % 2 !== 0) {\n throw new Error('Expected non-empty, even-length array.');\n }\n if (splicingIndices.some((index) => !Number.isInteger(index) || index < 0)) {\n throw new Error('Expected array of non-negative integers.');\n }\n\n const retainedSubstrings: string[] = [];\n\n // Get the first part to be included\n // The substring() call returns an empty string if splicingIndices[0] is 0,\n // which is exactly what we want in that case.\n retainedSubstrings.push(toSplice.substring(0, splicingIndices[0]));\n\n // This loop gets us all parts of the string that should be retained, except\n // the first and the last.\n // It iterates over all \"end\" indices of the array except the last one, and\n // pushes the substring between each \"end\" index and the next \"begin\" index\n // to the array of retained substrings.\n if (splicingIndices.length > 2) {\n // Note the boundary index of \"splicingIndices.length - 1\". This loop must\n // not iterate over the last element of the array, which is handled outside\n // of this loop.\n for (let i = 1; i < splicingIndices.length - 1; i += 2) {\n retainedSubstrings.push(\n // splicingIndices[i] refers to an element between the first and last\n // elements of the array, and will always be defined.\n toSplice.substring(splicingIndices[i], splicingIndices[i + 1]),\n );\n }\n }\n\n // Get the last part to be included\n retainedSubstrings.push(\n // The last element of a non-empty array will always be defined.\n toSplice.substring(splicingIndices[splicingIndices.length - 1]),\n );\n return retainedSubstrings.join('');\n}\n\n/**\n * Gets an invalid fence line error message.\n *\n * @param filePath - The path to the file that caused the error.\n * @param line - The contents of the line with the error.\n * @param details - An explanation of the error.\n * @returns The error message.\n */\nfunction getInvalidFenceLineMessage(\n filePath: string,\n line: string,\n details: string,\n) {\n return `Invalid fence line in file \"${filePath}\": \"${line}\":\\n${details}`;\n}\n\n/**\n * Gets an invalid fence structure error message.\n *\n * @param filePath - The path to the file that caused the error.\n * @param details - An explanation of the error.\n * @returns The error message.\n */\nfunction getInvalidFenceStructureMessage(filePath: string, details: string) {\n return `Invalid fence structure in file \"${filePath}\":\\n${details}`;\n}\n\n/**\n * Gets an invalid fence pair error message.\n *\n * @param filePath - The path to the file that caused the error.\n * @param line - The contents of the line with the error.\n * @param details - An explanation of the error.\n * @returns The error message.\n */\nfunction getInvalidFencePairMessage(\n filePath: string,\n line: string,\n details: string,\n) {\n return `Invalid fence pair in file \"${filePath}\" due to line \"${line}\":\\n${details}`;\n}\n\n/**\n * Gets an invalid command params error message.\n *\n * @param filePath - The path to the file that caused the error.\n * @param details - An explanation of the error.\n * @param command - The command of the directive with the invalid parameters, if known.\n * @returns The error message.\n */\nfunction getInvalidParamsMessage(\n filePath: string,\n details: string,\n command?: string,\n) {\n return `Invalid code fence parameters in file \"${filePath}\"${\n command ? `for command \"${command}\"` : ''\n }:\\n${details}`;\n}\n\n/**\n * Checks whether the given terminus string is valid, i.e. one of `BEGIN` or `END`.\n *\n * @param terminus - The terminus string to validate.\n * @returns Whether the string is a valid terminus string.\n */\nfunction isValidTerminus(terminus: string): terminus is DirectiveTerminus {\n return hasProperty(DirectiveTerminus, terminus);\n}\n\n/**\n * Checks whether the given command string is valid.\n *\n * @param command - The command string to validate.\n * @returns Whether the string is a valid command string.\n */\nfunction isValidCommand(command: string): command is DirectiveCommand {\n return hasProperty(DirectiveCommand, command);\n}\n\n/**\n * Validates the specified command. Throws if validation fails.\n *\n * @param command - The command to validate.\n * @param params - The parameters of the command.\n * @param filePath - The path of the current file.\n * @param featureLabels - The possible feature labels.\n */\nexport function validateCommand(\n command: unknown,\n params: string[],\n filePath: string,\n featureLabels: FeatureLabels,\n): asserts command is DirectiveCommand {\n switch (command) {\n case DirectiveCommand.ONLY_INCLUDE_IF:\n if (!params || params.length === 0) {\n throw new Error(\n getInvalidParamsMessage(\n filePath,\n `No parameters specified.`,\n DirectiveCommand.ONLY_INCLUDE_IF,\n ),\n );\n }\n\n for (const param of params) {\n if (!featureLabels.all.has(param)) {\n throw new Error(\n getInvalidParamsMessage(\n filePath,\n `\"${param}\" is not a declared build feature.`,\n DirectiveCommand.ONLY_INCLUDE_IF,\n ),\n );\n }\n }\n break;\n\n default:\n throw new Error(`Unrecognized command \"${String(command)}\".`);\n }\n}\n"]}
|