Feat#Provide a quick-fix for non-exported imports#72
Feat#Provide a quick-fix for non-exported imports#72Richard-Lynch wants to merge 12 commits intobloomberg:oss-hackathon-37440from
Conversation
Signed-off-by: Richard Lynch <rlynch79@bloomberg.net>
Signed-off-by: Richard Lynch <rlynch79@bloomberg.net>
Signed-off-by: Richard Lynch <rlynch79@bloomberg.net>
Signed-off-by: Richard Lynch <rlynch79@bloomberg.net>
82a3253 to
c68ae3b
Compare
|
Maybe worth adding (at)DanielRosenwasser and (at)sandersn when this is added to the TS repo |
mkubilayk
left a comment
There was a problem hiding this comment.
Looking very good - some minor comments.
| const node = localSymbol.valueDeclaration.parent; | ||
|
|
||
| return changes.insertExportModifier(sourceFile, node); | ||
| } |
There was a problem hiding this comment.
Do we need special handling for class declarations as well?
There was a problem hiding this comment.
class is captured/handled with isDeclarationStatement but I've added some tests and simplified that logic
| // If there is an existing export | ||
| if ( | ||
| namedExportDeclaration?.exportClause && | ||
| isNamedExports(namedExportDeclaration.exportClause) |
There was a problem hiding this comment.
Should we check if this is a type-only export? My understanding is that this would change:
export type { existingThing };to
export { existingThing, newThing };which may not be desired.
There was a problem hiding this comment.
nice! yes, this is a good test case, adding now
| if ( | ||
| isExportDeclaration(statement) && | ||
| statement.exportClause && | ||
| isNamedExports(statement.exportClause) |
There was a problem hiding this comment.
We should skip re-export statements here: export { a } from "./something";.
| factory.updateExportDeclaration( | ||
| /*node*/ namedExportDeclaration, | ||
| /*modifiers*/ undefined, | ||
| /*isTypeOnly*/ false, | ||
| /*exportClause*/ factory.updateNamedExports( | ||
| /*node*/ namedExportDeclaration.exportClause, | ||
| /*elements*/ sortSpecifiers( | ||
| namedExportDeclaration.exportClause.elements.concat( | ||
| factory.createExportSpecifier( | ||
| /*isTypeOnly*/ false, | ||
| /*propertyName*/ undefined, | ||
| node | ||
| ) | ||
| ) | ||
| ) | ||
| ), | ||
| /*moduleSpecifier*/ undefined, | ||
| /*assertClause*/ undefined | ||
| ) |
There was a problem hiding this comment.
| factory.updateExportDeclaration( | |
| /*node*/ namedExportDeclaration, | |
| /*modifiers*/ undefined, | |
| /*isTypeOnly*/ false, | |
| /*exportClause*/ factory.updateNamedExports( | |
| /*node*/ namedExportDeclaration.exportClause, | |
| /*elements*/ sortSpecifiers( | |
| namedExportDeclaration.exportClause.elements.concat( | |
| factory.createExportSpecifier( | |
| /*isTypeOnly*/ false, | |
| /*propertyName*/ undefined, | |
| node | |
| ) | |
| ) | |
| ) | |
| ), | |
| /*moduleSpecifier*/ undefined, | |
| /*assertClause*/ undefined | |
| ) | |
| factory.updateExportDeclaration( | |
| namedExportDeclaration, | |
| /*modifiers*/ undefined, | |
| /*isTypeOnly*/ false, | |
| factory.updateNamedExports( | |
| namedExportDeclaration.exportClause, | |
| sortSpecifiers( | |
| namedExportDeclaration.exportClause.elements.concat( | |
| factory.createExportSpecifier( | |
| /*isTypeOnly*/ false, | |
| /*propertyName*/ undefined, | |
| node | |
| ) | |
| ) | |
| ) | |
| ), | |
| /*moduleSpecifier*/ undefined, | |
| /*assertClause*/ undefined | |
| ) | |
I think some of these arguments are self explanatory.
There was a problem hiding this comment.
I'm happy to remove any of these you'd like, but tbh as someone new to the project => they're not :P
Honestly half the pain is having so many positional arguments at all!
| /*exportClause*/ factory.createNamedExports([ | ||
| factory.createExportSpecifier( | ||
| /*isTypeOnly*/ false, | ||
| /*propertyName*/ undefined, | ||
| node | ||
| ), | ||
| ]), |
There was a problem hiding this comment.
| /*exportClause*/ factory.createNamedExports([ | |
| factory.createExportSpecifier( | |
| /*isTypeOnly*/ false, | |
| /*propertyName*/ undefined, | |
| node | |
| ), | |
| ]), | |
| factory.createNamedExports([ | |
| factory.createExportSpecifier( | |
| /*isTypeOnly*/ false, | |
| /*propertyName*/ undefined, | |
| node | |
| ), | |
| ]), | |
| /*expression*/ !(isVariableDeclarationList(node) && node.declarations.length !== 1), | ||
| /*message*/ "Only allow adding export modifier to variable lists with 1 element" |
There was a problem hiding this comment.
| /*expression*/ !(isVariableDeclarationList(node) && node.declarations.length !== 1), | |
| /*message*/ "Only allow adding export modifier to variable lists with 1 element" | |
| !(isVariableDeclarationList(node) && node.declarations.length !== 1), | |
| "Only allow adding export modifier to variable lists with 1 element" | |
| ////let d = 4; | ||
| ////export function whatever() { | ||
| ////} | ||
| ////export { d } |
There was a problem hiding this comment.
Would be good to add some test cases where source file has some exports that we cannot use:
export type { a };
export { b } from "./b";
export * as c from "./c";
// ...| "code": 90058 | ||
| }, | ||
|
|
||
| "Export '{0}' from module '{1}'": { |
There was a problem hiding this comment.
| "Export '{0}' from module '{1}'": { | |
| "Export \"{0}\" from module \"{1}\"": { |
minor, seems to match the other ones above this.
There was a problem hiding this comment.
hmm, the Remove import from './b' uses single quotes, what do you think?
src/compiler/diagnosticMessages.json
Outdated
| "category": "Message", | ||
| "code": 90059 | ||
| }, | ||
| "Add all missing exports": { |
There was a problem hiding this comment.
I realise this message is simialr to the "Add all imports" one, but I don't think it really expresses the change that will happen. Maybe "Export all missing members in modules"
There was a problem hiding this comment.
Agreed, I changed to Export all missing members from modules
|
|
||
| // @Filename: /b.ts | ||
| ////declare function foo(): any; | ||
| ////function bar(): any; |
There was a problem hiding this comment.
I would like to see some tests on overloaded functions as well.
There was a problem hiding this comment.
Good shout! do you think the export should be on the implemented function or in a named export? 🤔
There was a problem hiding this comment.
Ok, I've given this a look and its actually a little tricky given the current implementation!
I can easily make all functions export using a named export => export {add};, but if we want to follow the convention of applying the export on the declaration where possible it'll take a bit more work! Do you think that's critical?
There was a problem hiding this comment.
I think a separate export statement (e.g. export { add }) would be acceptable for function overloads.
| index: 1, | ||
| description: `Export 'b' from module './a'`, | ||
| newFileContent: { | ||
| "/a.ts": `class a{}, export class b{}; |
There was a problem hiding this comment.
This is producing invalid syntax. I think we need to fallback to export { b }; in this case.


Fixes microsoft#37440
If:
The import is from a .d.ts file, do nothing.
The declaration is a single variable (e.g.
let a = 1): update toexport let a = 1The declaration is a single function (e.g.
function a(){...}): update toexport function a(){...}The declaration is in a list and contains multiple variables (e.g.
let a = 1, b = 2)export {...}found in the file: create anexport {...}and add to thatexport {...}found in the file: add to the lastexport {...}Testing performed
Local testing + tests added
Additional context
Based on this closed PR
I think I addressed all the comments except this, which seems reasonable to me!