"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createTemplateCodegenContext = createTemplateCodegenContext; const codeFeatures_1 = require("../codeFeatures"); const utils_1 = require("../utils"); const wrapWith_1 = require("../utils/wrapWith"); /** * Creates and returns a Context object used for generating type-checkable TS code * from the template section of a .vue file. * * ## Implementation Notes for supporting `@vue-ignore`, `@vue-expect-error`, and `@vue-skip` directives. * * Vue language tooling supports a number of directives for suppressing diagnostics within * Vue templates (https://github.com/vuejs/language-tools/pull/3215) * * Here is an overview for how support for how @vue-expect-error is implemented within this file * (@vue-expect-error is the most complicated directive to support due to its behavior of raising * a diagnostic when it is annotating a piece of code that doesn't actually have any errors/warning/diagnostics). * * Given .vue code: * * ```vue * * * * ``` * * The above code should raise two diagnostics: * * 1. Property 'error_unknownProp' does not exist on type [...] * 2. Unused '@ts-expect-error' directive.ts(2578) -- this is the bottom `@vue-expect-error` directive * that covers code that doesn't actually raise an error -- note that all `@vue-...` directives * will ultimately translate into `@ts-...` diagnostics. * * The above code will produce the following type-checkable TS code (note: omitting asterisks * to prevent VSCode syntax double-greying out double-commented code). * * ```ts * ( __VLS_ctx.knownProp1 ); * ( __VLS_ctx.error_unknownProp ); // ERROR: Property 'error_unknownProp' does not exist on type [...] * ( __VLS_ctx.knownProp2 ); * // @vue-expect-error start * ( __VLS_ctx.suppressed_error_unknownProp ); * // @ts-expect-error __VLS_TS_EXPECT_ERROR * ; * // @vue-expect-error end of INTERPOLATION * ( __VLS_ctx.knownProp3 ); * // @vue-expect-error start * ( __VLS_ctx.knownProp4_will_trigger_unused_expect_error ); * // @ts-expect-error __VLS_TS_EXPECT_ERROR * ; * // @vue-expect-error end of INTERPOLATION * ``` * * In the generated code, there are actually 3 diagnostic errors that'll be raised in the first * pass on this generated code (but through cleverness described below, not all of them will be * propagated back to the original .vue file): * * 1. Property 'error_unknownProp' does not exist on type [...] * 2. Unused '@ts-expect-error' directive.ts(2578) from the 1st `@ts-expect-error __VLS_TS_EXPECT_ERROR` * 3. Unused '@ts-expect-error' directive.ts(2578) from the 2nd `@ts-expect-error __VLS_TS_EXPECT_ERROR` * * Be sure to pay careful attention to the mixture of `@vue-expect-error` and `@ts-expect-error`; * Within the TS file, the only "real" directives recognized by TS are going to be prefixed with `@ts-`; * any `@vue-` prefixed directives in the comments are only for debugging purposes. * * As mentioned above, there are 3 diagnostics errors that'll be generated for the above code, but * only 2 should be propagated back to the original .vue file. * * (The reason we structure things this way is somewhat complicated, but in short it allows us * to lean on TS as much as possible to generate actual `unused @ts-expect-error directive` errors * while covering a number of edge cases.) * * So, we need a way to dynamically decide whether each of the `@ts-expect-error __VLS_TS_EXPECT_ERROR` * directives should be reported as an unused directive or not. * * To do this, we'll make use of the `shouldReport` callback that'll optionally be provided to the * `verification` property of the `CodeInformation` object attached to the mapping between source .vue * and generated .ts code. The `verification` property determines whether "verification" (which includes * semantic diagnostics) should be performed on the generated .ts code, and `shouldReport`, if provided, * can be used to determine whether a given diagnostic should be reported back "upwards" to the original * .vue file or not. * * See the comments in the code below for how and where we use this hook to keep track of whether * an error/diagnostic was encountered for a region of code covered by a `@vue-expect-error` directive, * and additionally how we use that to determine whether to propagate diagnostics back upward. */ function createTemplateCodegenContext(options) { let ignoredError = false; let expectErrorToken; let lastGenericComment; let variableId = 0; function resolveCodeFeatures(features) { if (features.verification) { if (ignoredError) { // We are currently in a region of code covered by a @vue-ignore directive, so don't // even bother performing any type-checking: set verification to false. return { ...features, verification: false, }; } if (expectErrorToken) { // We are currently in a region of code covered by a @vue-expect-error directive. We need to // keep track of the number of errors encountered within this region so that we can know whether // we will need to propagate an "unused ts-expect-error" diagnostic back to the original // .vue file or not. const token = expectErrorToken; return { ...features, verification: { shouldReport: () => { token.errors++; return false; }, }, }; } } return features; } const hoistVars = new Map(); const localVars = new Map(); const dollarVars = new Set(); const accessExternalVariables = new Map(); const slots = []; const dynamicSlots = []; const blockConditions = []; const scopedClasses = []; const emptyClassOffsets = []; const inlayHints = []; const bindingAttrLocs = []; const inheritedAttrVars = new Set(); const templateRefs = new Map(); return { codeFeatures: new Proxy(codeFeatures_1.codeFeatures, { get(target, key) { const data = target[key]; return resolveCodeFeatures(data); }, }), resolveCodeFeatures, slots, dynamicSlots, dollarVars, accessExternalVariables, lastGenericComment, blockConditions, scopedClasses, emptyClassOffsets, inlayHints, bindingAttrLocs, inheritedAttrVars, templateRefs, currentComponent: undefined, singleRootElTypes: [], singleRootNodes: new Set(), accessExternalVariable(name, offset) { let arr = accessExternalVariables.get(name); if (!arr) { accessExternalVariables.set(name, arr = new Set()); } if (offset !== undefined) { arr.add(offset); } }, hasLocalVariable: (name) => { return !!localVars.get(name); }, addLocalVariable: (name) => { localVars.set(name, (localVars.get(name) ?? 0) + 1); }, removeLocalVariable: (name) => { localVars.set(name, localVars.get(name) - 1); }, getInternalVariable: () => { return `__VLS_${variableId++}`; }, getHoistVariable: (originalVar) => { let name = hoistVars.get(originalVar); if (name === undefined) { hoistVars.set(originalVar, name = `__VLS_${variableId++}`); } return name; }, generateHoistVariables: function* () { // trick to avoid TS 4081 (#5186) if (hoistVars.size) { yield `// @ts-ignore${utils_1.newLine}`; yield `var `; for (const [originalVar, hoistVar] of hoistVars) { yield `${hoistVar} = ${originalVar}, `; } yield utils_1.endOfLine; } }, generateConditionGuards: function* () { for (const condition of blockConditions) { yield `if (!${condition}) return${utils_1.endOfLine}`; } }, ignoreError: function* () { if (!ignoredError) { ignoredError = true; yield `// @vue-ignore start${utils_1.newLine}`; } }, expectError: function* (prevNode) { if (!expectErrorToken) { expectErrorToken = { errors: 0, node: prevNode, }; yield `// @vue-expect-error start${utils_1.newLine}`; } }, resetDirectiveComments: function* (endStr) { if (expectErrorToken) { const token = expectErrorToken; yield* (0, wrapWith_1.wrapWith)(expectErrorToken.node.loc.start.offset, expectErrorToken.node.loc.end.offset, { verification: { // If no errors/warnings/diagnostics were reported within the region of code covered // by the @vue-expect-error directive, then we should allow any `unused @ts-expect-error` // diagnostics to be reported upward. shouldReport: () => token.errors === 0, }, }, `// @ts-expect-error __VLS_TS_EXPECT_ERROR`); yield `${utils_1.newLine}${utils_1.endOfLine}`; expectErrorToken = undefined; yield `// @vue-expect-error ${endStr}${utils_1.newLine}`; } if (ignoredError) { ignoredError = false; yield `// @vue-ignore ${endStr}${utils_1.newLine}`; } }, generateAutoImportCompletion: function* () { if (!options.edited) { return; } const all = [...accessExternalVariables.entries()]; if (!all.some(([_, offsets]) => offsets.size)) { return; } yield `// @ts-ignore${utils_1.newLine}`; // #2304 yield `[`; for (const [varName, offsets] of all) { for (const offset of offsets) { if (options.scriptSetupBindingNames.has(varName)) { // #3409 yield [ varName, 'template', offset, { ...codeFeatures_1.codeFeatures.additionalCompletion, ...codeFeatures_1.codeFeatures.withoutHighlightAndCompletionAndNavigation, }, ]; } else { yield [ varName, 'template', offset, codeFeatures_1.codeFeatures.additionalCompletion, ]; } yield `,`; } offsets.clear(); } yield `]${utils_1.endOfLine}`; } }; } //# sourceMappingURL=context.js.map