Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sourcemaps not working correctly with static properties #35307

Closed
christopheranderson opened this issue Nov 23, 2019 · 4 comments
Closed

sourcemaps not working correctly with static properties #35307

christopheranderson opened this issue Nov 23, 2019 · 4 comments
Assignees
Labels
Needs More Info The issue still hasn't been fully clarified

Comments

@christopheranderson
Copy link

TypeScript Version: 3.8.0-dev.20191122 (also reproduced on @latest (3.7.2)

Search Terms:
typescript sourcemaps not working with static properties

Code

This issue is specifically when trying to consume the generated sourcemaps in vscode.

From: https://github.com/microsoft/botframework-cli/blob/91881c3be6bf3888f97f86ff7d9b314871bf5d5b/packages/dialog/src/commands/dialog/verify.ts#L14-L29

If the below static properties are placed before other code in my class, then the sourcemap that's generated is off and debugging tools like VS code will have their breakpoints misplaced.

static args = [
        { name: 'glob1', required: true },
        { name: 'glob2', required: false },
        { name: 'glob3', required: false },
        { name: 'glob4', required: false },
        { name: 'glob5', required: false },
        { name: 'glob6', required: false },
        { name: 'glob7', required: false },
        { name: 'glob8', required: false },
        { name: 'glob9', required: false },
    ]

    static flags: flags.Input<any> = {
        help: flags.help({ char: 'h' }),
        verbose: flags.boolean({ description: 'Show verbose output', default: false }),
    }
Additional debugging info (including full source for the file and generated content.

(If you attempt to build the project for a local repro, you'll need to look at the contributor guide. We use rush to orchestrate building the projects).

launch.json snippet config for verify

{
            "type": "node",
            "request": "launch",
            "name": "Dialog Verify Tests",
            "program": "${workspaceFolder}/packages/dialog/node_modules/mocha/bin/_mocha",
            "cwd": "${workspaceFolder}/packages/dialog",
            "args": [
                "--timeout",
                "999999",
                "--colors",
                "-g",
                ".*dialog:verify.*"
            ],
            "internalConsoleOptions": "openOnSessionStart",
            "sourceMaps": true,
            "outFiles": [
                "./package/dialog/lib/**"
            ]
        },

generated files:

verify ts

/*!
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */

import { Command, flags } from '@microsoft/bf-cli-command';
import * as chalk from 'chalk';
import { Definition, DialogTracker, SchemaTracker } from '../../library/dialogTracker';

// import * as process from 'process';

export default class DialogVerify extends Command {

    static args = [
        { name: 'glob1', required: true },
        { name: 'glob2', required: false },
        { name: 'glob3', required: false },
        { name: 'glob4', required: false },
        { name: 'glob5', required: false },
        { name: 'glob6', required: false },
        { name: 'glob7', required: false },
        { name: 'glob8', required: false },
        { name: 'glob9', required: false },
    ]

    static flags: flags.Input<any> = {
        help: flags.help({ char: 'h' }),
        verbose: flags.boolean({ description: 'Show verbose output', default: false }),
    }

    private currentFile = ''
    private files = 0
    private errors = 0
    private warnings = 0

    async run() {
        const { argv, flags } = this.parse(DialogVerify)
        await this.execute(argv, flags.verbose)
    }

    async execute(dialogFiles: string[], verbose?: boolean): Promise<void> {
        const schema = new SchemaTracker()
        const tracker = new DialogTracker(schema)

        await tracker.addDialogFiles(dialogFiles)

        if (tracker.dialogs.length === 0) {
            this.error('No  dialogs found!')
        } else {
            for (let dialog of tracker.dialogs) {
                this.files++
                this.currentFile = dialog.file
                if (dialog.errors.length === 0) {
                    if (verbose) {
                        this.consoleLog(`${dialog}`)
                    }
                } else {
                    for (let error of dialog.errors) {
                        this.consoleError(`${error.message.trim()}`, 'DLG001')
                    }
                }
            }

            for (let defs of tracker.multipleDefinitions()) {
                let def = (defs as Definition[])[0]
                this.consoleError(`Multiple definitions for ${def} ${def.usedByString()}`, 'DLG002')
                for (let def of defs) {
                    this.consoleError(`  ${def.pathString()}`, 'DLG002')
                }
            }

            for (let def of tracker.missingDefinitions()) {
                this.consoleError(`Missing definition for ${def} ${def.usedByString()}`, 'DLG003')
            }

            for (let def of tracker.missingTypes) {
                this.consoleError(`Missing $type for ${def}`, 'DLG004')
            }

            for (let def of tracker.unusedIDs()) {
                this.consoleWarn(`Unused id ${def}`, 'DLG005')
            }

            if (verbose) {
                for (let [type, definitions] of tracker.typeToDef) {
                    this.consoleMsg(`Instances of ${type}`)
                    for (let def of definitions) {
                        this.consoleMsg(`  ${def.locatorString()}`)
                    }
                }
            }

            this.log(`${this.files} files processed.`)
            this.error(`${this.warnings} found.`)
            if (this.errors > 0) {
                this.error(`Error: ${this.errors} found.`)
            }
        }
    }

    consoleMsg(msg: string): void {
        this.log(chalk.default(msg))
    }

    consoleLog(msg: string): void {
        this.log(chalk.default.gray(msg))
    }

    consoleWarn(msg: string, code: string): void {
        this.warnings++
        this.warn(`${this.currentFile} - warning ${code || ''}: ${msg}`)
    }

    consoleError(msg: string, code: string): void {
        this.errors++
        this.error(`${this.currentFile} - error ${code || ''}: ${msg}`)
    }
}

verify js

"use strict";
/*!
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
const bf_cli_command_1 = require("@microsoft/bf-cli-command");
const chalk = require("chalk");
const dialogTracker_1 = require("../../library/dialogTracker");
// import * as process from 'process';
class DialogVerify extends bf_cli_command_1.Command {
    constructor() {
        super(...arguments);
        this.currentFile = '';
        this.files = 0;
        this.errors = 0;
        this.warnings = 0;
    }
    async run() {
        const { argv, flags } = this.parse(DialogVerify);
        await this.execute(argv, flags.verbose);
    }
    async execute(dialogFiles, verbose) {
        const schema = new dialogTracker_1.SchemaTracker();
        const tracker = new dialogTracker_1.DialogTracker(schema);
        await tracker.addDialogFiles(dialogFiles);
        if (tracker.dialogs.length === 0) {
            this.error('No  dialogs found!');
        }
        else {
            for (let dialog of tracker.dialogs) {
                this.files++;
                this.currentFile = dialog.file;
                if (dialog.errors.length === 0) {
                    if (verbose) {
                        this.consoleLog(`${dialog}`);
                    }
                }
                else {
                    for (let error of dialog.errors) {
                        this.consoleError(`${error.message.trim()}`, 'DLG001');
                    }
                }
            }
            for (let defs of tracker.multipleDefinitions()) {
                let def = defs[0];
                this.consoleError(`Multiple definitions for ${def} ${def.usedByString()}`, 'DLG002');
                for (let def of defs) {
                    this.consoleError(`  ${def.pathString()}`, 'DLG002');
                }
            }
            for (let def of tracker.missingDefinitions()) {
                this.consoleError(`Missing definition for ${def} ${def.usedByString()}`, 'DLG003');
            }
            for (let def of tracker.missingTypes) {
                this.consoleError(`Missing $type for ${def}`, 'DLG004');
            }
            for (let def of tracker.unusedIDs()) {
                this.consoleWarn(`Unused id ${def}`, 'DLG005');
            }
            if (verbose) {
                for (let [type, definitions] of tracker.typeToDef) {
                    this.consoleMsg(`Instances of ${type}`);
                    for (let def of definitions) {
                        this.consoleMsg(`  ${def.locatorString()}`);
                    }
                }
            }
            this.log(`${this.files} files processed.`);
            this.error(`${this.warnings} found.`);
            if (this.errors > 0) {
                this.error(`Error: ${this.errors} found.`);
            }
        }
    }
    consoleMsg(msg) {
        this.log(chalk.default(msg));
    }
    consoleLog(msg) {
        this.log(chalk.default.gray(msg));
    }
    consoleWarn(msg, code) {
        this.warnings++;
        this.warn(`${this.currentFile} - warning ${code || ''}: ${msg}`);
    }
    consoleError(msg, code) {
        this.errors++;
        this.error(`${this.currentFile} - error ${code || ''}: ${msg}`);
    }
}
exports.default = DialogVerify;
DialogVerify.args = [
    { name: 'glob1', required: true },
    { name: 'glob2', required: false },
    { name: 'glob3', required: false },
    { name: 'glob4', required: false },
    { name: 'glob5', required: false },
    { name: 'glob6', required: false },
    { name: 'glob7', required: false },
    { name: 'glob8', required: false },
    { name: 'glob9', required: false },
];
DialogVerify.flags = {
    help: bf_cli_command_1.flags.help({ char: 'h' }),
    verbose: bf_cli_command_1.flags.boolean({ description: 'Show verbose output', default: false }),
};
//# sourceMappingURL=verify.js.map

verify.js.map

{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAEH,8DAA2D;AAE3D,MAAqB,KAAM,SAAQ,wBAAO;IAOtC,KAAK,CAAC,GAAG;QACL,IAAI,CAAC,KAAK,EAAE,CAAA;IAChB,CAAC;;AATL,wBAUC;AATU,iBAAW,GAAG,2DAA2D,CAAA;AAEzE,WAAK,GAAqB;IAC7B,IAAI,EAAE,sBAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;CAClC,CAAA"}

Expected behavior:

When I set a breakpoint on line 37 & 42, I'd expect it to hit that.

Actual behavior:

It's off by 3 (40 and 45) & it actually thinks its somewhere deep in the execute method.

image

Workaround:

Move the static properties to the bottom of the class and everything works as expected.

Playground Link: N/A

Related Issues: N/A

@rbuckton
Copy link
Member

I'm having a hard time reproducing this. Are you able to craft a minimal repro that does not require imports/logic specific to your project? The repro I attempted to create seems to work fine when debugging and breakpoints are hit, and I've verified that the source maps in question properly line up with between the original sources and the generated outputs.

image

image

image

@RyanCavanaugh RyanCavanaugh removed this from the TypeScript 3.8.1 milestone Jan 24, 2020
@RyanCavanaugh RyanCavanaugh added Needs More Info The issue still hasn't been fully clarified and removed Bug A bug in TypeScript labels Jan 24, 2020
@christopheranderson
Copy link
Author

We have a repro in our botframework-cli project, but I'll try to craft a minimal one over the weekend. Thanks for pinging me on this, Ryan.

@zuhairtaha
Copy link

I resolved this issue by updating the target to es2022 or any version beyond.
Alternatively, you can simply choose esnext, and the generated JavaScript file will use static methods without encountering this problem.

@RyanCavanaugh
Copy link
Member

If someone has a concrete repro, please file a new issue with it. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs More Info The issue still hasn't been fully clarified
Projects
None yet
Development

No branches or pull requests

4 participants