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

Add ${relativeFileNoExtension} to 'predefined common variables' #25786

Closed
mmkal opened this issue May 2, 2017 · 14 comments
Closed

Add ${relativeFileNoExtension} to 'predefined common variables' #25786

mmkal opened this issue May 2, 2017 · 14 comments
Assignees
Labels
debug Debug viewlet, configurations, breakpoints, adapter issues feature-request Request for new features or functionality help wanted Issues identified as good community contribution opportunities *out-of-scope Posted issue is not in scope of VS Code tasks Task system issues
Milestone

Comments

@mmkal
Copy link

mmkal commented May 2, 2017

  • VSCode Version:
    1.11.2
  • OS Version:
    any
    Steps to Reproduce:

I'm trying to make a launch configuration that will start debugging the currently-opened TypeScript file using ava
Right now I can't, because you need to pass it args a bit like this in launch.json:

{
    "program": "${workspaceRoot}/node_modules/ava/profile.js",
    "args": ["${workspaceRoot}/path/to/my/file.js"]
}

So if you want to run the currently opened test file, you'd use something like:
"args": ["${workspaceRoot}/${relativeFile}"]

Which works fine if you're writing plain JavaScript, because ava will be able to run it directly. But if you're using TypeScript, ava will try to interpret the TypeScript as JavaScript and will barf.

If we had relativeFileNoExtension, it'd be pretty easy to make a configuration that ran the current file:
"args": ["${workspaceRoot}/dist/${relativeFileNoExtension}.js"]

For example, if I have foo/bar/tests/mytestfile.ts open, we'd have relativeFileNoExtension being foo/bar/tests/mytestfile and the arg would be /path/to/my/workspace/dist/foo/bar/tests/mytestfile.js, which matches the JavaScript output from tsc.
where dist is the outDir in tsconfig.json - obviously this part is project-specific.

I'd imagine the implementation would be something like this in configurationResolveService:

private get relativeFileNoExtension(): string {
    const relativeFile = this.relativeFile();
    return relativeFile.slice(0, relativeFile.length - paths.extname(relativeFile).length);
}
@kieferrm kieferrm added the feature-request Request for new features or functionality label May 2, 2017
@isidorn
Copy link
Contributor

isidorn commented May 3, 2017

There is fileBasenameNoExtension please try using that.
More variables can be found here https://code.visualstudio.com/docs/editor/tasks#_variable-substitution

@isidorn isidorn closed this as completed May 3, 2017
@mmkal
Copy link
Author

mmkal commented May 7, 2017

@isidorn unless I am missing something, that doesn't solve my problem. In the example I've given (i.e. /path/to/my/workspace/dist/foo/bar/tests/mytestfile.js), that would give me mytestfile. What about the relative directory, i.e. foo/bar/tests? I need that information too.

@isidorn
Copy link
Contributor

isidorn commented May 8, 2017

Yes it seems that is currently not possible
If more users ask for this we would be open for PRs
Code pointer https://github.com/Microsoft/vscode/blob/master/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts#L41

@isidorn isidorn reopened this May 8, 2017
@isidorn isidorn added the help wanted Issues identified as good community contribution opportunities label May 8, 2017
@isidorn isidorn added this to the Backlog milestone May 8, 2017
@isidorn isidorn removed their assignment May 8, 2017
@unional
Copy link

unional commented Jun 5, 2017

I'm able to get it to "almost" work. Need to know how to run the ava/profile.js inside another script file in the same process. Do you know how to do that?

I have:

// launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Run AVA test",
      "program": "${workspaceRoot}/scripts/debug.js",
      // "program": "${workspaceRoot}/node_modules/ava/profile.js",
      "preLaunchTask": "tsc",
      "sourceMaps": true,
      "args": [
        "${file}"
      ]
    }
  ]
}

// scripts/debug.js
'use strict';

const cp = require('child_process');
const path = require('path');

const outDir = 'dist-es5'

function getOutFilepath() {
  let filepath = path.relative(process.cwd() + '/src', process.argv[2]);
  let extension = path.extname(filepath)
  filepath = filepath.slice(0, -extension.length) + (extension === '.ts' ? '.js' : '.jsx')
  return filepath = `${outDir}/${filepath}`
}

const filepath = getOutFilepath()
process.argv[2] = filepath

// this doesn't pick up by vscode.
// It is run and I can see the result in DEBUG CONSOLE, but breakpoint not hit
cp.spawnSync('node', ['./node_modules/ava/profile.js', filepath], {
  stdio: 'inherit',
  shell: true
})

unional added a commit to clibuilder/clibuilder that referenced this issue Jun 5, 2017
unional added a commit to clibuilder/clibuilder that referenced this issue Jun 5, 2017
* Add create function.

Properly separate main boundary (`create()`) and application boundary (`Cli`).

This is the reason why I am having problem with UI, the dependency is mixed togther and hard to deal with.

* Code clean up

* Add show version

* Update all tests to acceptance tests

* Add changes for debugging with ava

Not ready yet. Need help on microsoft/vscode#25786 (comment)

* remove

* Add missing wordwrap dep
Clean up
@danielweck
Copy link

I am able to use the Visual Studio Code debugger to step through my TypeScript-written AVA unit tests. Caveat: the breakpoints can be placed on the TS code, but the debugger actually steps through the generated JS code ... I'm not sure why.


launch.json

   {
      "name": "test-ava",
      "trace": "all",
      "type": "node",
      "request": "launch",
      "program": "${workspaceRoot}/test/ava.ts",
      "stopOnEntry": false,
      "cwd": "${workspaceRoot}",
      "internalConsoleOptions": "openOnSessionStart",
      "args": [
        "--verbose",
        "--fail-fast",
        "--serial",
        "${workspaceRoot}/dist/test/test.js"
      ],
      "env": {
        "DEBUG": "*",
        "NODE_DEBUG": "1",
        "NODE_ENV": "development"
      },
      "console": "internalConsole",
      "sourceMaps": true,
      "outFiles": [
        "${workspaceRoot}/dist/**/*.js"
      ]
    }

test/ava.ts

// This is a straight port to TypeScript of ./nodes_modules/ava/profile.js

import * as EventEmitter from "events";
import * as path from "path";

import * as  arrify from "arrify";
import * as  Promise from "bluebird";
import * as  findCacheDir from "find-cache-dir";
import * as  meow from "meow";
import * as  pkgConf from "pkg-conf";
import * as  resolveCwd from "resolve-cwd";
import * as  uniqueTempDir from "unique-temp-dir";

import * as  babelConfigHelper from "ava/lib/babel-config";
import * as  CachingPrecompiler from "ava/lib/caching-precompiler";
import * as  globals from "ava/lib/globals";

function resolveModules(modules: any) {
    return arrify(modules).map((name: any) => {
        const modulePath = resolveCwd(name);

        if (modulePath === null) {
            throw new Error(`Could not resolve required module '${name}'`);
        }

        return modulePath;
    });
}

// Chrome gets upset when the `this` value is non-null for these functions
globals.setTimeout = setTimeout.bind(null);
globals.clearTimeout = clearTimeout.bind(null);

Promise.longStackTraces();

const conf = pkgConf.sync("ava", {
    defaults: {
        babel: "default",
    },
});

// Define a minimal set of options from the main CLI
const cli = meow(`
Usage
    $ iron-node node_modules/ava/profile.js <test-file>
Options
    --fail-fast   Stop after first test failure
    --serial, -s  Run tests serially
`, {
        alias: {
            s: "serial",
        },
        boolean: [
            "fail-fast",
            "verbose",
            "serial",
            "tap",
        ],
        default: conf,
        string: [
            "_",
        ],
    });

if (cli.input.length !== 1) {
    throw new Error("Specify a test file");
}

const file = path.resolve(cli.input[0]);
const cacheDir = findCacheDir({
    files: [file],
    name: "ava",
}) || uniqueTempDir();

babelConfigHelper.build(process.cwd(), cacheDir, conf.babel, true)
    .then((result: any) => {
        const precompiler = new CachingPrecompiler({
            babelCacheKeys: result.cacheKeys,
            getBabelOptions: result.getOptions,
            path: cacheDir,
        });

        const precompiled: any = {};
        precompiled[file] = precompiler.precompileFile(file);

        const opts = {
            cacheDir,
            failFast: cli.flags.failFast,
            file,
            precompiled,
            require: resolveModules(conf.require),
            serial: cli.flags.serial,
            tty: false,
        };

        const events = new EventEmitter();
        let uncaughtExceptionCount = 0;

        // Mock the behavior of a parent process
        (process as any).channel = {
            // tslint:disable-next-line:no-empty
            ref() {
            },
            // tslint:disable-next-line:no-empty
            unref() {
            },
        };
        process.send = (data: any) => {
            if (data && data.ava) {
                const name = data.name.replace(/^ava-/, "");

                if (events.listeners(name).length > 0) {
                    events.emit(name, data.data);
                } else {
                    console.log("UNHANDLED AVA EVENT:", name, data.data);
                }

                return;
            }

            console.log("NON AVA EVENT:", data);
        };

        events.on("test", (data: any) => {
            console.log("TEST:", data.title, data.error);
        });

        events.on("results", (data: any) => {
            if (console.profileEnd) {
                console.profileEnd();
            }

            console.log("RESULTS:", data.stats);

            if (process.exit) {
                // eslint-disable-next-line unicorn/no-process-exit
                process.exit(data.stats.failCount + uncaughtExceptionCount);
            }
        });

        events.on("stats", () => {
            setImmediate(() => {
                process.emit("ava-run", {});
            });
        });

        events.on("uncaughtException", (data: any) => {
            uncaughtExceptionCount++;
            let stack = data && data.exception && data.exception.stack;
            stack = stack || data;
            console.log(stack);
        });

        // `test-worker` will read process.argv[2] for options
        process.argv[2] = JSON.stringify(opts);
        process.argv.length = 3;

        if (console.profile) {
            console.profile("AVA test-worker process");
        }

        setImmediate(() => {
            // eslint-disable-next-line import/no-unassigned-import
            require("ava/lib/test-worker");
        });
    });

declarations.d.ts

declare module "arrify";
declare module "bluebird";
declare module "find-cache-dir";
declare module "meow";
declare module "pkg-conf";
declare module "resolve-cwd";
declare module "unique-temp-dir";
declare module "ava/lib/babel-config";
declare module "ava/lib/caching-precompiler";
declare module "ava/lib/globals";

@bitjson
Copy link

bitjson commented May 29, 2018

Has anyone found a solution to this? It's also needed here in typescript-starter.

@bitjson
Copy link

bitjson commented May 30, 2018

I took a swing at it here: #50758 (docs here: microsoft/vscode-docs#1648)

I think that PR enables all the use cases mentioned in this issue so far. 👍

@weinand
Copy link
Contributor

weinand commented Aug 22, 2018

Instead of adding yet another variable with very specific semantics to the growing set of variables, I suggest to create an extension that contribute a command with the specific semantics.
Commands can be used in launch configs through the ${command:<command-id>} syntax.

@bitjson
Copy link

bitjson commented Aug 22, 2018

@weinand thanks for the response and consideration!

I'd be happy to take a swing at implementing this as an extension. I thought this was best included in the built-in variable set because relativePath seems to be the only missing element required to construct any other path.

We already have workspaceFolderBasename, fileDirname, fileBasenameNoExtension, and fileExtname. I think the only element not provided by these variables is a relative path without the file name.

Unfortunately, relativeFile (folder/file.ext) comes close but prevents some uses. Without the final file.ext, we'd be able to express any path.

@RolfRolles
Copy link

RolfRolles commented Oct 8, 2018

I'm having a similar issue. I have a fairly large Python project with multiple directories. I.e., all of the Python files need to be invoked as modules relative to the ${workspaceFolder}. I.e., if I want to debug ${workspaceFolder}/Tests/EvalPrint/TestEvalPrint.py, I need to tell launch.py that the module name is "Tests.EvalPrint.TestEvalPrint".

It looks like none of my current options, nor the one proposed herein, will solve this for me. ${fileBasenameNoExtension} will give me TestEvalPrint. The proposed ${relativeFileNoExtension} would give me Tests/EvalPrint/TestEvalPrint. I need "Tests.EvalPrint.TestEvalPrint".

I'd be happy to write a bit of code to solve this for my specific use-case (although I suspect it would be common for any multi-directory Python project that requires absolute module names); I don't necessarily need an officially-supported pre-defined variable. Is there an option for this that allows the user to generate the JSON variable definitions programmatically?

@abhi8893
Copy link

abhi8893 commented Aug 7, 2019

I'm having a similar issue. I have a fairly large Python project with multiple directories. I.e., all of the Python files need to be invoked as modules relative to the ${workspaceFolder}. I.e., if I want to debug ${workspaceFolder}/Tests/EvalPrint/TestEvalPrint.py, I need to tell launch.py that the module name is "Tests.EvalPrint.TestEvalPrint".

It looks like none of my current options, nor the one proposed herein, will solve this for me. ${fileBasenameNoExtension} will give me TestEvalPrint. The proposed ${relativeFileNoExtension} would give me Tests/EvalPrint/TestEvalPrint. I need "Tests.EvalPrint.TestEvalPrint".

I'd be happy to write a bit of code to solve this for my specific use-case (although I suspect it would be common for any multi-directory Python project that requires absolute module names); I don't necessarily need an officially-supported pre-defined variable. Is there an option for this that allows the user to generate the JSON variable definitions programmatically?

I need exactly this! Refer to my question here.
Did you find a solution?

@weinand
Copy link
Contributor

weinand commented Aug 7, 2019

I'm citing myself:

Instead of adding yet another variable with very specific semantics to the growing set of variables, I suggest to create an extension that contributes a command with the specific semantics.
Commands can be used in launch configs through the ${command:<command-id>} syntax.

@jrieken jrieken added debug Debug viewlet, configurations, breakpoints, adapter issues tasks Task system issues labels Oct 8, 2019
@isidorn isidorn self-assigned this Oct 9, 2019
@isidorn isidorn added the *out-of-scope Posted issue is not in scope of VS Code label Oct 9, 2019
@vscodebot
Copy link

vscodebot bot commented Oct 9, 2019

This issue is being closed to keep the number of issues in our inbox on a manageable level, we are closing issues that are not going to be addressed in the foreseeable future: We look at the number of votes the issue has received and the number of duplicate issues filed. More details here. If you disagree and feel that this issue is crucial: We are happy to listen and to reconsider.

If you wonder what we are up to, please see our roadmap and issue reporting guidelines.

Thanks for your understanding and happy coding!

@vscodebot vscodebot bot closed this as completed Oct 9, 2019
@jcrben
Copy link

jcrben commented Feb 12, 2020

Note for anyone who finds this via Google: this was ultimately fixed by the combination of relativeFileDirname & fileBasenameNoExtension - see #50758

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
debug Debug viewlet, configurations, breakpoints, adapter issues feature-request Request for new features or functionality help wanted Issues identified as good community contribution opportunities *out-of-scope Posted issue is not in scope of VS Code tasks Task system issues
Projects
None yet
Development

Successfully merging a pull request may close this issue.