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

Support 'import.meta' #23327

Merged
merged 16 commits into from
Apr 28, 2018
Merged

Support 'import.meta' #23327

merged 16 commits into from
Apr 28, 2018

Conversation

DanielRosenwasser
Copy link
Member

@DanielRosenwasser DanielRosenwasser commented Apr 10, 2018

Fixes #22861. This PR introduces support for import.meta, a meta-property described here.

Here are some details on the implementation.

The ImportMeta Interface

The type of import.meta is the global ImportMeta type which is defined in lib.es5.d.ts:

interface ImportMeta {
}

This interface is extremely limited. Adding well-known properties for Node or browsers requires interface merging and possibly a global augmentation depending on the context.

For example, one might want to say that __dirname is always available on import.meta:

// node.d.ts
interface ImportMeta {
    __dirname: string;
}

Any user of the above .d.ts file will be able to get completions for import.meta.__dirname which has the type string.

Alternatively, you might be in a bind and just need to say "yes, I know import.meta will have this property, and I only need to use it in one file":

// declaring a property ad-hoc/offhandedly
declare global  {
    interface ImportMeta {
        mySuperCoolProperty: string;
    }
}

import.meta.mySuperCoolProperty // Has type 'string', not 'any'.

An earlier version of this pull request defined ImportMeta as follows:

interface ImportMeta {
    [propertyName: string]: any;
}

However, this interface is extremely lax, and means that you can misspell any property.

Community feedback is appreciated here.

Targets

import.meta is only allowed when targeting ESNext modules and ECMAScript targets.

I think it might make sense to say "this only runs on ESNext modules, but can run on any target", but I'd rather be conservative for now.

Incremental Parsing

import.meta is only valid within a module. This implies one of two things:

  1. It is an error to use import.meta if the current module does not contain a module indicator (i.e. the module has no imports/exports).
  2. import.meta itself is a module indicator (which turns the current file into a module).

I went with choice (2), which implies that a module indicator can occur anywhere within the file. To avoid a full walk of the tree, I use a new NodeFlag called PossiblyContainsImportMeta. Much like NodeFlags.PossiblyContainsDynamicImport, this new flag is set exactly once, and is never unset when re-using an AST.

When the flag is set, we will do a full walk of the AST to find module indicators, but in the common case, we'll run into an import or export declaration.

src/lib/es5.d.ts Outdated
@@ -505,6 +505,10 @@ interface TemplateStringsArray extends ReadonlyArray<string> {
readonly raw: ReadonlyArray<string>;
}

interface ImportMeta {
[propertyName: string]: any;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no way to opt out of this one... users who want strict definition can not undo the index signature. consider not adding it and letting say node.d.ts add it.

}

function walkTreeForExternalModuleIndicators(node: Node): Node {
return isAnExternalModuleIndicatorNode(node) ? node : forEachChild(node, walkTreeForExternalModuleIndicators);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can accidentally pick up an import/export from an module augmentation as the current file's externalModuleIndicator if we're not careful.

declare module "foo" {
  import * as blah from "blah"
}

import.meta

@e111077
Copy link

e111077 commented Apr 26, 2018

Heya, we are using stackblitz to host our documentation demos on our site before Google I/O, and we believe that we are blocked on this issue. Do you know which typescript release this fix would be included in?

CC: @EricSimons @justinfagnani

Relevant issues:
stackblitz/core#378
Polymer/old-docs-site#2537

@weswigham
Copy link
Member

2.9 would be our next feature release, which should be cut sometime near the end of May.

// and possibly nested import statements in the future).
// Ideally the first few statements will be an import/export anyway.
sourceFile.externalModuleIndicator =
!(sourceFile.flags & NodeFlags.PossiblyContainsImportMeta) ?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this should be:

sourceFile.externalModuleIndicator = forEach(sourceFile.statements, isAnExternalModuleIndicatorNode) || getImportMetaNode(sourceFile);

function isAnExternalModuleIndicatorNode(node: Node) {
    return hasModifier(node, ModifierFlags.Export)
        || node.kind === SyntaxKind.ImportEqualsDeclaration && (<ImportEqualsDeclaration>node).moduleReference.kind === SyntaxKind.ExternalModuleReference
        || node.kind === SyntaxKind.ImportDeclaration
        || node.kind === SyntaxKind.ExportAssignment
        || node.kind === SyntaxKind.ExportDeclaration
        ? node
        : undefined
}

function isImportMetaNode(node: Node) { 
    return isMetaProperty(node) && node.keywordToken === SyntaxKind.ImportKeyword && node.name.escapedText === "meta";
}

function walkTreeForExternalModuleIndicators(node: Node): Node {
    return isImportMetaNode(node) ? node : forEachChild(node, walkTreeForExternalModuleIndicators);
}

function getImportMetaNode(node: SourceFile) { 
    return (sourceFile.flags & NodeFlags.PossiblyContainsImportMeta) ? walkTreeForExternalModuleIndicators(node) : undefined;
} 

@mhegazy
Copy link
Contributor

mhegazy commented Apr 26, 2018

Please take a look at the checklist in https://github.com/Microsoft/TypeScript/wiki/Release-Activities#new-syntax-introduced.

we need to add colorization support as well.

@DanielRosenwasser
Copy link
Member Author

We can get this in first and discuss relaxing the restriction after.

@microsoft microsoft locked and limited conversation to collaborators Jul 31, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants