Skip to content

Commit f557c05

Browse files
Import assertions (jashkenas#5391)
1 parent 20b8362 commit f557c05

File tree

21 files changed

+735
-210
lines changed

21 files changed

+735
-210
lines changed

Cakefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,7 @@ runTests = (CoffeeScript) ->
476476
skipUnless '/foo.bar/s.test("foo\tbar")', ['regex_dotall.coffee']
477477
skipUnless '1_2_3', ['numeric_literal_separators.coffee']
478478
skipUnless '1n', ['numbers_bigint.coffee']
479+
skipUnless 'async () => { await import(\'data:application/json,{"foo":"bar"}\', { assert: { type: "json" } }) }', ['import_assertions.coffee']
479480
files = fs.readdirSync('test').filter (filename) ->
480481
filename not in testFilesToSkip
481482

docs/v2/annotated-source/grammar.html

Lines changed: 29 additions & 19 deletions
Large diffs are not rendered by default.

docs/v2/annotated-source/lexer.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,9 @@ <h2 id="tokenizers">Tokenizers</h2>
414414
<span class="hljs-keyword">if</span> id <span class="hljs-keyword">is</span> <span class="hljs-string">&#x27;default&#x27;</span> <span class="hljs-keyword">and</span> @seenExport <span class="hljs-keyword">and</span> @tag() <span class="hljs-keyword">in</span> [<span class="hljs-string">&#x27;EXPORT&#x27;</span>, <span class="hljs-string">&#x27;AS&#x27;</span>]
415415
@token <span class="hljs-string">&#x27;DEFAULT&#x27;</span>, id
416416
<span class="hljs-keyword">return</span> id.length
417+
<span class="hljs-keyword">if</span> id <span class="hljs-keyword">is</span> <span class="hljs-string">&#x27;assert&#x27;</span> <span class="hljs-keyword">and</span> (@seenImport <span class="hljs-keyword">or</span> @seenExport) <span class="hljs-keyword">and</span> @tag() <span class="hljs-keyword">is</span> <span class="hljs-string">&#x27;STRING&#x27;</span>
418+
@token <span class="hljs-string">&#x27;ASSERT&#x27;</span>, id
419+
<span class="hljs-keyword">return</span> id.length
417420
<span class="hljs-keyword">if</span> id <span class="hljs-keyword">is</span> <span class="hljs-string">&#x27;do&#x27;</span> <span class="hljs-keyword">and</span> regExSuper = <span class="hljs-regexp">/^(\s*super)(?!\(\))/</span>.exec @chunk[<span class="hljs-number">3.</span>..]
418421
@token <span class="hljs-string">&#x27;SUPER&#x27;</span>, <span class="hljs-string">&#x27;super&#x27;</span>
419422
@token <span class="hljs-string">&#x27;CALL_START&#x27;</span>, <span class="hljs-string">&#x27;(&#x27;</span>

docs/v2/annotated-source/nodes.html

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5845,11 +5845,11 @@ <h3 id="import-and-export">Import and Export</h3>
58455845

58465846
<div class="content"><div class='highlight'><pre>
58475847
<span class="hljs-built_in">exports</span>.ModuleDeclaration = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ModuleDeclaration</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
5848-
constructor: <span class="hljs-function"><span class="hljs-params">(@clause, @source)</span> -&gt;</span>
5848+
constructor: <span class="hljs-function"><span class="hljs-params">(@clause, @source, @assertions)</span> -&gt;</span>
58495849
super()
58505850
@checkSource()
58515851

5852-
children: [<span class="hljs-string">&#x27;clause&#x27;</span>, <span class="hljs-string">&#x27;source&#x27;</span>]
5852+
children: [<span class="hljs-string">&#x27;clause&#x27;</span>, <span class="hljs-string">&#x27;source&#x27;</span>, <span class="hljs-string">&#x27;assertions&#x27;</span>]
58535853

58545854
isStatement: YES
58555855
jumps: THIS
@@ -5880,6 +5880,14 @@ <h3 id="import-and-export">Import and Export</h3>
58805880
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> o.indent.length <span class="hljs-keyword">isnt</span> <span class="hljs-number">0</span>
58815881
@error <span class="hljs-string">&quot;<span class="hljs-subst">#{moduleDeclarationType}</span> statements must be at top-level scope&quot;</span>
58825882

5883+
astAssertions: <span class="hljs-function"><span class="hljs-params">(o)</span> -&gt;</span>
5884+
<span class="hljs-keyword">if</span> @assertions?.properties?
5885+
@assertions.properties.map (assertion) =&gt;
5886+
{ start, end, loc, left, right } = assertion.ast(o)
5887+
{ type: <span class="hljs-string">&#x27;ImportAttribute&#x27;</span>, start, end, loc, key: left, value: right }
5888+
<span class="hljs-keyword">else</span>
5889+
[]
5890+
58835891
<span class="hljs-built_in">exports</span>.ImportDeclaration = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ImportDeclaration</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ModuleDeclaration</span></span>
58845892
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -&gt;</span>
58855893
@checkScope o, <span class="hljs-string">&#x27;import&#x27;</span>
@@ -5892,6 +5900,9 @@ <h3 id="import-and-export">Import and Export</h3>
58925900
<span class="hljs-keyword">if</span> @source?.value?
58935901
code.push @makeCode <span class="hljs-string">&#x27; from &#x27;</span> <span class="hljs-keyword">unless</span> @clause <span class="hljs-keyword">is</span> <span class="hljs-literal">null</span>
58945902
code.push @makeCode @source.value
5903+
<span class="hljs-keyword">if</span> @assertions?
5904+
code.push @makeCode <span class="hljs-string">&#x27; assert &#x27;</span>
5905+
code.push @assertions.compileToFragments(o)...
58955906

58965907
code.push @makeCode <span class="hljs-string">&#x27;;&#x27;</span>
58975908
code
@@ -5904,6 +5915,7 @@ <h3 id="import-and-export">Import and Export</h3>
59045915
ret =
59055916
specifiers: @clause?.ast(o) ? []
59065917
source: @source.ast o
5918+
assertions: @astAssertions(o)
59075919
ret.importKind = <span class="hljs-string">&#x27;value&#x27;</span> <span class="hljs-keyword">if</span> @clause
59085920
ret
59095921

@@ -5965,7 +5977,12 @@ <h3 id="import-and-export">Import and Export</h3>
59655977
<span class="hljs-keyword">else</span>
59665978
code = code.concat @clause.compileNode o
59675979

5968-
code.push @makeCode <span class="hljs-string">&quot; from <span class="hljs-subst">#{@source.value}</span>&quot;</span> <span class="hljs-keyword">if</span> @source?.value?
5980+
<span class="hljs-keyword">if</span> @source?.value?
5981+
code.push @makeCode <span class="hljs-string">&quot; from <span class="hljs-subst">#{@source.value}</span>&quot;</span>
5982+
<span class="hljs-keyword">if</span> @assertions?
5983+
code.push @makeCode <span class="hljs-string">&#x27; assert &#x27;</span>
5984+
code.push @assertions.compileToFragments(o)...
5985+
59695986
code.push @makeCode <span class="hljs-string">&#x27;;&#x27;</span>
59705987
code</pre></div></div>
59715988

@@ -5994,6 +6011,7 @@ <h3 id="import-and-export">Import and Export</h3>
59946011
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -&gt;</span>
59956012
ret =
59966013
source: @source?.ast(o) ? <span class="hljs-literal">null</span>
6014+
assertions: @astAssertions(o)
59976015
exportKind: <span class="hljs-string">&#x27;value&#x27;</span>
59986016
clauseAst = @clause.ast o
59996017
<span class="hljs-keyword">if</span> @clause <span class="hljs-keyword">instanceof</span> ExportSpecifierList
@@ -6008,11 +6026,13 @@ <h3 id="import-and-export">Import and Export</h3>
60086026
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -&gt;</span>
60096027
<span class="hljs-keyword">return</span>
60106028
declaration: @clause.ast o
6029+
assertions: @astAssertions(o)
60116030

60126031
<span class="hljs-built_in">exports</span>.ExportAllDeclaration = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ExportAllDeclaration</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ExportDeclaration</span></span>
60136032
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -&gt;</span>
60146033
<span class="hljs-keyword">return</span>
60156034
source: @source.ast o
6035+
assertions: @astAssertions(o)
60166036
exportKind: <span class="hljs-string">&#x27;value&#x27;</span>
60176037

60186038
<span class="hljs-built_in">exports</span>.ModuleSpecifierList = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ModuleSpecifierList</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
@@ -6148,8 +6168,8 @@ <h3 id="import-and-export">Import and Export</h3>
61486168
super o
61496169

61506170
checkArguments: <span class="hljs-function">-&gt;</span>
6151-
<span class="hljs-keyword">unless</span> @args.length <span class="hljs-keyword">is</span> <span class="hljs-number">1</span>
6152-
@error <span class="hljs-string">&#x27;import() requires exactly one argument&#x27;</span>
6171+
<span class="hljs-keyword">unless</span> <span class="hljs-number">1</span> &lt;= @args.length &lt;= <span class="hljs-number">2</span>
6172+
@error <span class="hljs-string">&#x27;import() accepts either one or two arguments&#x27;</span>
61536173

61546174
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -&gt;</span>
61556175
@checkArguments()

docs/v2/browser-compiler-legacy/coffeescript.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/v2/browser-compiler-modern/coffeescript.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/v2/index.html

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4268,6 +4268,8 @@ <h2>Modules</h2>
42684268
import { first, last } from 'underscore'
42694269
import utilityBelt, { each } from 'underscore'
42704270

4271+
import dates from './calendar.json' assert { type: 'json' }
4272+
42714273
export default Math
42724274
export square = (x) -> x * x
42734275
export class Mathematics
@@ -4279,6 +4281,7 @@ <h2>Modules</h2>
42794281

42804282
export * from 'underscore'
42814283
export { max, min } from 'underscore'
4284+
export { version } from './package.json' assert { type: 'json' }
42824285
</textarea>
42834286
<pre class="placeholder-code"><span class="cm-variable">import</span> <span class="cm-string">'./local-file.js'</span> <span class="cm-comment"># Must be the filename of the generated file</span>
42844287
<span class="cm-variable">import</span> <span class="cm-string">'package'</span>
@@ -4291,6 +4294,8 @@ <h2>Modules</h2>
42914294
<span class="cm-variable">import</span> <span class="cm-punctuation">{</span> <span class="cm-variable">first</span><span class="cm-punctuation">,</span> <span class="cm-variable">last</span> <span class="cm-punctuation">}</span> <span class="cm-variable">from</span> <span class="cm-string">'underscore'</span>
42924295
<span class="cm-variable">import</span> <span class="cm-variable">utilityBelt</span><span class="cm-punctuation">,</span> <span class="cm-punctuation">{</span> <span class="cm-variable">each</span> <span class="cm-punctuation">}</span> <span class="cm-variable">from</span> <span class="cm-string">'underscore'</span>
42934296

4297+
<span class="cm-variable">import</span> <span class="cm-variable">dates</span> <span class="cm-variable">from</span> <span class="cm-string">'./calendar.json'</span> <span class="cm-variable">assert</span> <span class="cm-punctuation">{</span> <span class="cm-variable">type</span><span class="cm-punctuation">:</span> <span class="cm-string">'json'</span> <span class="cm-punctuation">}</span>
4298+
42944299
<span class="cm-variable">export</span> <span class="cm-variable">default</span> <span class="cm-variable">Math</span>
42954300
<span class="cm-variable">export</span> <span class="cm-variable">square</span> <span class="cm-punctuation">=</span> <span class="cm-punctuation">(</span><span class="cm-variable">x</span><span class="cm-punctuation">)</span> <span class="cm-operator">-></span> <span class="cm-variable">x</span> <span class="cm-operator">*</span> <span class="cm-variable">x</span>
42964301
<span class="cm-variable">export</span> <span class="cm-keyword">class</span> <span class="cm-variable">Mathematics</span>
@@ -4302,6 +4307,7 @@ <h2>Modules</h2>
43024307

43034308
<span class="cm-variable">export</span> <span class="cm-operator">*</span> <span class="cm-variable">from</span> <span class="cm-string">'underscore'</span>
43044309
<span class="cm-variable">export</span> <span class="cm-punctuation">{</span> <span class="cm-variable">max</span><span class="cm-punctuation">,</span> <span class="cm-variable">min</span> <span class="cm-punctuation">}</span> <span class="cm-variable">from</span> <span class="cm-string">'underscore'</span>
4310+
<span class="cm-variable">export</span> <span class="cm-punctuation">{</span> <span class="cm-variable">version</span> <span class="cm-punctuation">}</span> <span class="cm-variable">from</span> <span class="cm-string">'./package.json'</span> <span class="cm-variable">assert</span> <span class="cm-punctuation">{</span> <span class="cm-variable">type</span><span class="cm-punctuation">:</span> <span class="cm-string">'json'</span> <span class="cm-punctuation">}</span>
43054311
</pre>
43064312
</div>
43074313
<div class="col-md-6 javascript-output-column">
@@ -4330,6 +4336,10 @@ <h2>Modules</h2>
43304336
each
43314337
} from 'underscore';
43324338

4339+
import dates from './calendar.json' assert {
4340+
type: 'json'
4341+
};
4342+
43334343
export default Math;
43344344

43354345
export var square = function(x) {
@@ -4366,6 +4376,12 @@ <h2>Modules</h2>
43664376
max,
43674377
min
43684378
} from 'underscore';
4379+
4380+
export {
4381+
version
4382+
} from './package.json' assert {
4383+
type: 'json'
4384+
};
43694385
</textarea>
43704386
<pre class="placeholder-code"><span class="cm-keyword">import</span> <span class="cm-string">'./local-file.js'</span>;
43714387

@@ -4392,6 +4408,10 @@ <h2>Modules</h2>
43924408
<span class="cm-def">each</span>
43934409
} <span class="cm-keyword">from</span> <span class="cm-string">'underscore'</span>;
43944410

4411+
<span class="cm-keyword">import</span> <span class="cm-def">dates</span> <span class="cm-keyword">from</span> <span class="cm-string">'./calendar.json'</span> <span class="cm-variable">assert</span> {
4412+
<span class="cm-variable">type</span>: <span class="cm-string">'json'</span>
4413+
};
4414+
43954415
<span class="cm-keyword">export</span> <span class="cm-keyword">default</span> <span class="cm-variable">Math</span>;
43964416

43974417
<span class="cm-keyword">export</span> <span class="cm-keyword">var</span> <span class="cm-def">square</span> <span class="cm-operator">=</span> <span class="cm-keyword">function</span>(<span class="cm-def">x</span>) {
@@ -4428,6 +4448,12 @@ <h2>Modules</h2>
44284448
<span class="cm-variable">max</span>,
44294449
<span class="cm-variable">min</span>
44304450
} <span class="cm-keyword">from</span> <span class="cm-string">'underscore'</span>;
4451+
4452+
<span class="cm-keyword">export</span> {
4453+
<span class="cm-variable">version</span>
4454+
} <span class="cm-keyword">from</span> <span class="cm-string">'./package.json'</span> <span class="cm-variable">assert</span> {
4455+
<span class="cm-variable">type</span>: <span class="cm-string">'json'</span>
4456+
};
44314457
</pre>
44324458
</div>
44334459
</div>

docs/v2/test.html

Lines changed: 142 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2218,6 +2218,31 @@ <h1>CoffeeScript Test Suite</h1>
22182218
type: 'StringLiteral'
22192219
value: '.'
22202220

2221+
testStatement 'import X from "." assert { type: "json" }',
2222+
type: 'ImportDeclaration'
2223+
specifiers: [
2224+
type: 'ImportDefaultSpecifier'
2225+
local:
2226+
type: 'Identifier'
2227+
name: 'X'
2228+
declaration: no
2229+
]
2230+
importKind: 'value'
2231+
source:
2232+
type: 'StringLiteral'
2233+
value: '.'
2234+
assertions: [
2235+
type: 'ImportAttribute'
2236+
key:
2237+
type: 'Identifier'
2238+
name: 'type'
2239+
value:
2240+
type: 'StringLiteral'
2241+
value: 'json'
2242+
extra:
2243+
raw: '"json"'
2244+
]
2245+
22212246
test "AST as expected for ImportDeclaration node", ->
22222247
testStatement 'import React, {Component} from "react"',
22232248
type: 'ImportDeclaration'
@@ -2369,6 +2394,26 @@ <h1>CoffeeScript Test Suite</h1>
23692394
raw: '"module-name"'
23702395
exportKind: 'value'
23712396

2397+
testStatement 'export * from "module-name" assert { type: "json" }',
2398+
type: 'ExportAllDeclaration'
2399+
source:
2400+
type: 'StringLiteral'
2401+
value: 'module-name'
2402+
extra:
2403+
raw: '"module-name"'
2404+
assertions: [
2405+
type: 'ImportAttribute'
2406+
key:
2407+
type: 'Identifier'
2408+
name: 'type'
2409+
value:
2410+
type: 'StringLiteral'
2411+
value: 'json'
2412+
extra:
2413+
raw: '"json"'
2414+
]
2415+
exportKind: 'value'
2416+
23722417
test "AST as expected for ExportSpecifierList node", ->
23732418
testStatement 'export {a, b, c}',
23742419
type: 'ExportNamedDeclaration'
@@ -22988,21 +23033,21 @@ <h1>CoffeeScript Test Suite</h1>
2298823033
^
2298923034
'''
2299023035

22991-
test "#4834: dynamic import requires exactly one argument", ->
23036+
test "#4834: dynamic import accepts either one or two arguments", ->
2299223037
assertErrorFormat '''
2299323038
import()
2299423039
''', '''
22995-
[stdin]:1:1: error: import() requires exactly one argument
23040+
[stdin]:1:1: error: import() accepts either one or two arguments
2299623041
import()
2299723042
^^^^^^^^
2299823043
'''
2299923044

2300023045
assertErrorFormat '''
23001-
import('x', {})
23046+
import('x', {}, 3)
2300223047
''', '''
23003-
[stdin]:1:1: error: import() requires exactly one argument
23004-
import('x', {})
23005-
^^^^^^^^^^^^^^^
23048+
[stdin]:1:1: error: import() accepts either one or two arguments
23049+
import('x', {}, 3)
23050+
^^^^^^^^^^^^^^^^^^
2300623051
'''
2300723052

2300823053
test "#4834: dynamic import requires explicit call parentheses", ->
@@ -25667,6 +25712,97 @@ <h1>CoffeeScript Test Suite</h1>
2566725712
filename = name + ext
2566825713
eq filename, expectedFileName
2566925714

25715+
</script>
25716+
<script type="text/x-coffeescript" class="test" id="import_assertions">
25717+
# This file is running in CommonJS (in Node) or as a classic Script (in the browser tests) so it can use import() within an async function, but not at the top level; and we can’t use static import.
25718+
test "dynamic import assertion", ->
25719+
{ default: secret } = await import('data:application/json,{"ofLife":42}', { assert: { type: 'json' } })
25720+
eq secret.ofLife, 42
25721+
25722+
test "assert keyword", ->
25723+
assert = 1
25724+
25725+
{ default: assert } = await import('data:application/json,{"thatIAm":42}', { assert: { type: 'json' } })
25726+
eq assert.thatIAm, 42
25727+
25728+
eqJS """
25729+
import assert from 'regression-test'
25730+
""", """
25731+
import assert from 'regression-test';
25732+
"""
25733+
25734+
25735+
test "static import assertion", ->
25736+
eqJS """
25737+
import 'data:application/json,{"foo":3}' assert { type: 'json' }
25738+
""", """
25739+
import 'data:application/json,{"foo":3}' assert {
25740+
type: 'json'
25741+
};
25742+
"""
25743+
25744+
eqJS """
25745+
import secret from 'data:application/json,{"ofLife":42}' assert { type: 'json' }
25746+
""", """
25747+
import secret from 'data:application/json,{"ofLife":42}' assert {
25748+
type: 'json'
25749+
};
25750+
"""
25751+
25752+
eqJS """
25753+
import * as secret from 'data:application/json,{"ofLife":42}' assert { type: 'json' }
25754+
""", """
25755+
import * as secret from 'data:application/json,{"ofLife":42}' assert {
25756+
type: 'json'
25757+
};
25758+
"""
25759+
25760+
# The only file types for which import assertions are currently supported are JSON (Node and browsers) and CSS (browsers), neither of which support named exports; however there’s nothing in the JavaScript grammar preventing a future supported file type from providing named exports.
25761+
eqJS """
25762+
import { foo } from './file.unknown' assert { type: 'unknown' }
25763+
""", """
25764+
import {
25765+
foo
25766+
} from './file.unknown' assert {
25767+
type: 'unknown'
25768+
};
25769+
"""
25770+
25771+
eqJS """
25772+
import file, { foo } from './file.unknown' assert { type: 'unknown' }
25773+
""", """
25774+
import file, {
25775+
foo
25776+
} from './file.unknown' assert {
25777+
type: 'unknown'
25778+
};
25779+
"""
25780+
25781+
eqJS """
25782+
import foo from 'bar' assert {}
25783+
""", """
25784+
import foo from 'bar' assert {};
25785+
"""
25786+
25787+
test "static export with assertion", ->
25788+
eqJS """
25789+
export * from 'data:application/json,{"foo":3}' assert { type: 'json' }
25790+
""", """
25791+
export * from 'data:application/json,{"foo":3}' assert {
25792+
type: 'json'
25793+
};
25794+
"""
25795+
25796+
eqJS """
25797+
export { profile } from './user.json' assert { type: 'json' }
25798+
""", """
25799+
export {
25800+
profile
25801+
} from './user.json' assert {
25802+
type: 'json'
25803+
};
25804+
"""
25805+
2567025806
</script>
2567125807
<script type="text/x-coffeescript" class="test" id="importing">
2567225808
# Importing

documentation/examples/modules.coffee

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { now as currentTimestamp } from 'underscore'
99
import { first, last } from 'underscore'
1010
import utilityBelt, { each } from 'underscore'
1111

12+
import dates from './calendar.json' assert { type: 'json' }
13+
1214
export default Math
1315
export square = (x) -> x * x
1416
export class Mathematics
@@ -20,3 +22,4 @@ export { Mathematics as default, sqrt as squareRoot }
2022

2123
export * from 'underscore'
2224
export { max, min } from 'underscore'
25+
export { version } from './package.json' assert { type: 'json' }

0 commit comments

Comments
 (0)