I'm using VSCode and trying to correct the way imports are written across multiple files in my project to a more performant format - does VSCode have functionality that would facilitate this? Could it be done with the built-in find and replace? Or is some other VSCode feature able to do this?
The imports I've got look like this (substance-ux is an obfuscated version of a real module name, as I don't want module specific answers):
import { Foo, Bar as BarBar } from '@substance-ux/glyphs';
Or maybe:
import {
GlyphWithLongName as LongName,
GlyphWithExtraLongName as ExtraLong
} from '@substance-ux/glyphs';
And I need to convert it into this style, matching imports elsewhere in our project:
import Foo from '@substance-ux/glyphs/Foo';
import BarBar from '@substance-ux/glyphs/Bar';
Or this:
import LongName from '@substance-ux/glyphs/GlyphWithLongName';
import ExtraLong from '@substance-ux/glyphs/GlyphWithExtraLongName';
(As an aside, the files like '@substance-ux/glyphs/GlyphWithExtraLongName' already exist and the docs for the package say that the @substance-ux/glyphs module runs a lot of code on import, which slows down development builds.)
Now if I know I have one format or the other, or I know how many then I'm ok to rely on find and replace, e.g. I can use a bit of regex (find: import \{ (.*), (.*) } from '(@substance-ux/glyphs)'; replace import $1 from '$3/$1';\nimport $2 from '$3/$2';) and the Find and Replace feature in VSCode.
But if I have variable number of imports, or mixed style (some 'as' some not) I become completely unstuck, if I try to do this in one go.
I know snippets can do regex capture and some clever replacing using TextMate syntax, but I don't think they can handle variable number of capture groups? Or can they?
Is this possible in VSCode without extensions etc?
Snippets can handle a variable number of arguments - I have answered a number of SO questions showing that - but I don't think they would work with your format where the arguments are already there rather than to be entered as part of the snippet. I really don't think there is a non-extension or non-script solution to your situation.
Using an extension that I wrote, Find and Transform, you can write javascript in a keybinding or a setting.
This keybinding - in your keybindings.json - will do what you want (it could also be a setting so that the command would appear in the Command Palette):
{
"key": "alt+f", // whatever keybinding you want
"command": "findInCurrentFile",
"args": {
"find": "(import\\s*{\\s*)([^}]+?)(\\s*}\\s*from\\s*')([^']+)';",
"replace": [
"$${",
"const from = `$4`;", // capture group 4 from the find
// using capture group 2 from the find regex
// note backticks around $2 below because it could contain newlines
"const splitImports = [...`$2`.matchAll(/(\\w+)(?:$|,)|(\\w+)\\s?as\\s?(\\w+)/g)];",
"let str = '';",
"splitImports.forEach((match, index) => { ",
// don't add a newline if last match
"let newline = '';",
"if (index < splitImports.length - 1) newline = '\\n';",
// note double-escapes necessary
"if (match[1]) str += `import ${match[1]} from \\'${from}/${match[1]}\\';${newline}`;",
"if (match[2]) str += `import ${match[3]} from \\'${from}/${match[2]}\\';${newline}`;",
"}); ",
"return str;",
"}$$"
],
"isRegex": true,
// "restrictFind": "line",
},
// "when": "editorLangId == javascript" // if you want it
}
This would work on the entire file as the demo shows. If you wanted to test it or just have it do the lines you select, enable "restrictFind": "line".
I don't understand what the vscode program has to do with your code.. The program won't actively change the structure of your code, you will have to do it manually:
You have this:
import { Foo, Bar as BarBar } from '@substance-ux/glyphs';
And you want this:
import Foo from '@substance-ux/glyphs/Foo';
import BarBar from '@substance-ux/glyphs/Bar';
The @substance-ux/glyphs file is having inside it multiple named exports, so
if you want to split things, you will need to create dedicated files which
exports default, so create a Foo.js file which export default Foo; and more all the code from @substance-ux/glyphs which is relevant to Foo to the new file, or just import from within Foo.js and re-expose as the default:
Foo.js file:import { Foo } from '@substance-ux/glyphs';
export default Foo;
Bar.js file:import { Bar } from '@substance-ux/glyphs';
export default Bar;
You can import any default exported thing by any name you wish:
import Banana from '@substance-ux/glyphs/Bar';
But I don't see why would you want to do this.. You didn't explain to us what is bad with importing multiple things from a single file. Treeshaking?