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

Conversation

Projects
None yet
4 participants
@DanielRosenwasser
Member

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.

@@ -505,6 +505,10 @@ interface TemplateStringsArray extends ReadonlyArray<string> {
readonly raw: ReadonlyArray<string>;
}
interface ImportMeta {
[propertyName: string]: any;

This comment has been minimized.

@mhegazy

mhegazy Apr 18, 2018

Contributor

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);

This comment has been minimized.

@DanielRosenwasser

DanielRosenwasser Apr 18, 2018

Member

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

This comment has been minimized.

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/docs#2537

@weswigham

This comment has been minimized.

Member

weswigham commented Apr 26, 2018

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) ?

This comment has been minimized.

@mhegazy

mhegazy Apr 26, 2018

Contributor

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

This comment has been minimized.

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.

@weswigham

This comment has been minimized.

Member

weswigham commented on src/compiler/checker.ts in 161535b Apr 28, 2018

I think the emit module kind is all we care about. The language version doesn't really matter, as the import.meta transform would be a module-related transform if we could downleveled it, I think.

@DanielRosenwasser

This comment has been minimized.

Member

DanielRosenwasser commented Apr 28, 2018

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

@DanielRosenwasser DanielRosenwasser merged commit 443c1c7 into master Apr 28, 2018

9 checks passed

TypeScript Test Run typescript_node.6 Build finished.
Details
TypeScript Test Run typescript_node.8 Build finished.
Details
TypeScript Test Run typescript_node.stable Build finished.
Details
VSTS: Typescript-Public-CI 20180427.21 succeeded
Details
ci/circleci: node6 Your tests passed on CircleCI!
Details
ci/circleci: node8 Your tests passed on CircleCI!
Details
ci/circleci: node9 Your tests passed on CircleCI!
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details
license/cla All CLA requirements met.
Details

@DanielRosenwasser DanielRosenwasser deleted the importDotMeta branch Apr 28, 2018

smockle added a commit to smockle/contrast that referenced this pull request Jun 26, 2018

@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.