mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(ast): implement new proposal-import-attributes (#1476)
- [Import Attributes](https://tc39.es/proposal-import-attributes)
This commit is contained in:
parent
46d1086c3e
commit
9ff0ffcc6f
11 changed files with 186 additions and 101 deletions
|
|
@ -1912,8 +1912,8 @@ pub struct ImportDeclaration<'a> {
|
|||
/// `None` for `import 'foo'`, `Some([])` for `import {} from 'foo'`
|
||||
pub specifiers: Option<Vec<'a, ImportDeclarationSpecifier>>,
|
||||
pub source: StringLiteral,
|
||||
pub assertions: Option<Vec<'a, ImportAttribute>>, // Some(vec![]) for empty assertion
|
||||
pub import_kind: ImportOrExportKind, // `import type { foo } from 'bar'`
|
||||
pub with_clause: Option<WithClause<'a>>, // Some(vec![]) for empty assertion
|
||||
pub import_kind: ImportOrExportKind, // `import type { foo } from 'bar'`
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash)]
|
||||
|
|
@ -1958,6 +1958,15 @@ pub struct ImportNamespaceSpecifier {
|
|||
pub local: BindingIdentifier,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))]
|
||||
pub struct WithClause<'a> {
|
||||
#[cfg_attr(feature = "serde", serde(flatten))]
|
||||
pub span: Span,
|
||||
pub attributes_keyword: IdentifierName, // `with` or `assert`
|
||||
pub with_entries: Vec<'a, ImportAttribute>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))]
|
||||
pub struct ImportAttribute {
|
||||
|
|
@ -2027,8 +2036,8 @@ pub struct ExportAllDeclaration<'a> {
|
|||
pub span: Span,
|
||||
pub exported: Option<ModuleExportName>,
|
||||
pub source: StringLiteral,
|
||||
pub assertions: Option<Vec<'a, ImportAttribute>>, // Some(vec![]) for empty assertion
|
||||
pub export_kind: ImportOrExportKind, // `export type *`
|
||||
pub with_clause: Option<WithClause<'a>>, // Some(vec![]) for empty assertion
|
||||
pub export_kind: ImportOrExportKind, // `export type *`
|
||||
}
|
||||
|
||||
impl<'a> ExportAllDeclaration<'a> {
|
||||
|
|
|
|||
|
|
@ -985,10 +985,10 @@ impl<'a> AstBuilder<'a> {
|
|||
span: Span,
|
||||
specifiers: Option<Vec<'a, ImportDeclarationSpecifier>>,
|
||||
source: StringLiteral,
|
||||
assertions: Option<Vec<'a, ImportAttribute>>,
|
||||
with_clause: Option<WithClause<'a>>,
|
||||
import_kind: ImportOrExportKind,
|
||||
) -> Box<'a, ImportDeclaration<'a>> {
|
||||
self.alloc(ImportDeclaration { span, specifiers, source, assertions, import_kind })
|
||||
self.alloc(ImportDeclaration { span, specifiers, source, with_clause, import_kind })
|
||||
}
|
||||
|
||||
pub fn export_all_declaration(
|
||||
|
|
@ -996,10 +996,10 @@ impl<'a> AstBuilder<'a> {
|
|||
span: Span,
|
||||
exported: Option<ModuleExportName>,
|
||||
source: StringLiteral,
|
||||
assertions: Option<Vec<'a, ImportAttribute>>,
|
||||
with_clause: Option<WithClause<'a>>,
|
||||
export_kind: ImportOrExportKind,
|
||||
) -> Box<'a, ExportAllDeclaration<'a>> {
|
||||
self.alloc(ExportAllDeclaration { span, exported, source, assertions, export_kind })
|
||||
self.alloc(ExportAllDeclaration { span, exported, source, with_clause, export_kind })
|
||||
}
|
||||
|
||||
pub fn export_default_declaration(
|
||||
|
|
|
|||
|
|
@ -649,7 +649,7 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for ImportDeclaration<'a> {
|
|||
p.print(b'\'');
|
||||
p.print_str(self.source.value.as_bytes());
|
||||
p.print(b'\'');
|
||||
self.assertions.gen(p, ctx);
|
||||
self.with_clause.gen(p, ctx);
|
||||
p.print_semicolon_after_statement();
|
||||
return;
|
||||
}
|
||||
|
|
@ -713,17 +713,23 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for ImportDeclaration<'a> {
|
|||
p.print_str(b" from ");
|
||||
}
|
||||
self.source.gen(p, ctx);
|
||||
self.assertions.gen(p, ctx);
|
||||
self.with_clause.gen(p, ctx);
|
||||
p.print_semicolon_after_statement();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, const MINIFY: bool> Gen<MINIFY> for Option<Vec<'a, ImportAttribute>> {
|
||||
impl<'a, const MINIFY: bool> Gen<MINIFY> for Option<WithClause<'a>> {
|
||||
fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) {
|
||||
if let Some(assertions) = &self {
|
||||
p.print_str(b"assert");
|
||||
p.print_block(assertions, Separator::Comma, ctx);
|
||||
};
|
||||
if let Some(with_clause) = self {
|
||||
with_clause.gen(p, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, const MINIFY: bool> Gen<MINIFY> for WithClause<'a> {
|
||||
fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) {
|
||||
self.attributes_keyword.gen(p, ctx);
|
||||
p.print_block(&self.with_entries, Separator::Comma, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -804,7 +810,7 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for ExportAllDeclaration<'a> {
|
|||
|
||||
p.print_str(b" from");
|
||||
self.source.gen(p, ctx);
|
||||
self.assertions.gen(p, ctx);
|
||||
self.with_clause.gen(p, ctx);
|
||||
|
||||
p.print_semicolon_after_statement();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -571,7 +571,7 @@ impl<'a> Gen for ImportDeclaration<'a> {
|
|||
p.print(b'\'');
|
||||
p.print_str(self.source.value.as_bytes());
|
||||
p.print(b'\'');
|
||||
self.assertions.gen(p);
|
||||
self.with_clause.gen(p);
|
||||
p.print_semicolon_after_statement();
|
||||
return;
|
||||
}
|
||||
|
|
@ -644,19 +644,25 @@ impl<'a> Gen for ImportDeclaration<'a> {
|
|||
p.print_str(b" from ");
|
||||
}
|
||||
self.source.gen(p);
|
||||
self.assertions.gen(p);
|
||||
self.with_clause.gen(p);
|
||||
p.print_semicolon_after_statement();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Gen for Option<Vec<'a, ImportAttribute>> {
|
||||
impl<'a> Gen for Option<WithClause<'a>> {
|
||||
fn gen(&self, p: &mut Formatter) {
|
||||
if let Some(assertions) = &self {
|
||||
if let Some(with_clause) = self {
|
||||
p.print_space();
|
||||
p.print_str(b"assert");
|
||||
with_clause.attributes_keyword.gen(p);
|
||||
p.print_space();
|
||||
p.print_block(assertions, Separator::Comma);
|
||||
};
|
||||
with_clause.with_entries.gen(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Gen for Vec<'a, ImportAttribute> {
|
||||
fn gen(&self, p: &mut Formatter) {
|
||||
p.print_block(self, Separator::Comma);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -735,7 +741,7 @@ impl<'a> Gen for ExportAllDeclaration<'a> {
|
|||
p.print_str(b" from");
|
||||
p.print_space();
|
||||
self.source.gen(p);
|
||||
self.assertions.gen(p);
|
||||
self.with_clause.gen(p);
|
||||
|
||||
p.print_semicolon_after_statement();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,14 +58,14 @@ impl<'a> Parser<'a> {
|
|||
};
|
||||
|
||||
let source = self.parse_literal_string()?;
|
||||
let assertions = self.parse_import_attributes()?;
|
||||
let with_clause = self.parse_import_attributes()?;
|
||||
self.asi()?;
|
||||
let span = self.end_span(span);
|
||||
let decl = ModuleDeclaration::ImportDeclaration(self.ast.import_declaration(
|
||||
span,
|
||||
specifiers,
|
||||
source,
|
||||
assertions,
|
||||
with_clause,
|
||||
import_kind,
|
||||
));
|
||||
Ok(self.ast.module_declaration(decl))
|
||||
|
|
@ -135,19 +135,23 @@ impl<'a> Parser<'a> {
|
|||
Ok(specifiers)
|
||||
}
|
||||
|
||||
/// [Import assertion](https://tc39.es/proposal-import-assertions)
|
||||
fn parse_import_attributes(&mut self) -> Result<Option<Vec<'a, ImportAttribute>>> {
|
||||
if !self.at(Kind::Assert) || self.cur_token().is_on_new_line {
|
||||
return Ok(None);
|
||||
}
|
||||
self.bump_any();
|
||||
/// [Import Attributes](https://tc39.es/proposal-import-attributes)
|
||||
fn parse_import_attributes(&mut self) -> Result<Option<WithClause<'a>>> {
|
||||
let span = self.start_span();
|
||||
let attributes_keyword = match self.cur_kind() {
|
||||
Kind::Assert if !self.cur_token().is_on_new_line => self.parse_identifier_name()?,
|
||||
Kind::With => self.parse_identifier_name()?,
|
||||
_ => {
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
|
||||
let ctx = self.ctx;
|
||||
self.ctx = Context::default();
|
||||
let entries = AssertEntries::parse(self)?.elements;
|
||||
let with_entries = AssertEntries::parse(self)?.elements;
|
||||
self.ctx = ctx;
|
||||
|
||||
Ok(Some(entries))
|
||||
Ok(Some(WithClause { span: self.end_span(span), attributes_keyword, with_entries }))
|
||||
}
|
||||
|
||||
pub(crate) fn parse_ts_export_assignment_declaration(
|
||||
|
|
@ -365,10 +369,10 @@ impl<'a> Parser<'a> {
|
|||
let exported = self.eat(Kind::As).then(|| self.parse_module_export_name()).transpose()?;
|
||||
self.expect(Kind::From)?;
|
||||
let source = self.parse_literal_string()?;
|
||||
let assertions = self.parse_import_attributes()?;
|
||||
let with_clause = self.parse_import_attributes()?;
|
||||
self.asi()?;
|
||||
let span = self.end_span(span);
|
||||
Ok(self.ast.export_all_declaration(span, exported, source, assertions, export_kind))
|
||||
Ok(self.ast.export_all_declaration(span, exported, source, with_clause, export_kind))
|
||||
}
|
||||
|
||||
// ImportSpecifier :
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
codegen_test262 Summary:
|
||||
AST Parsed : 44738/44738 (100.00%)
|
||||
Positive Passed: 44738/44738 (100.00%)
|
||||
AST Parsed : 45668/45668 (100.00%)
|
||||
Positive Passed: 45668/45668 (100.00%)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
minifier_test262 Summary:
|
||||
AST Parsed : 44738/44738 (100.00%)
|
||||
Positive Passed: 44738/44738 (100.00%)
|
||||
AST Parsed : 45668/45668 (100.00%)
|
||||
Positive Passed: 45668/45668 (100.00%)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
parser_test262 Summary:
|
||||
AST Parsed : 44161/44161 (100.00%)
|
||||
Positive Passed: 44161/44161 (100.00%)
|
||||
Negative Passed: 3918/3918 (100.00%)
|
||||
AST Parsed : 45097/45097 (100.00%)
|
||||
Positive Passed: 45097/45097 (100.00%)
|
||||
Negative Passed: 3925/3929 (99.90%)
|
||||
Expect Syntax Error: "language/import/import-assertions/json-invalid.js"
|
||||
Expect Syntax Error: "language/import/import-assertions/json-named-bindings.js"
|
||||
Expect Syntax Error: "language/import/import-attributes/json-invalid.js"
|
||||
Expect Syntax Error: "language/import/import-attributes/json-named-bindings.js"
|
||||
× '0'-prefixed octal literals and octal escape sequences are deprecated
|
||||
╭─[annexB/language/expressions/template-literal/legacy-octal-escape-sequence-strict.js:17:1]
|
||||
17 │
|
||||
|
|
@ -12971,13 +12975,6 @@ Negative Passed: 3918/3918 (100.00%)
|
|||
· ─────────────────
|
||||
╰────
|
||||
|
||||
× The keyword 'yield' is reserved
|
||||
╭─[language/expressions/dynamic-import/2nd-param-yield-ident-invalid.js:18:1]
|
||||
18 │
|
||||
19 │ import('./empty_FIXTURE.js', yield);
|
||||
· ─────
|
||||
╰────
|
||||
|
||||
× Keywords cannot contain escape characters
|
||||
╭─[language/expressions/dynamic-import/escape-sequence-import.js:34:1]
|
||||
34 │
|
||||
|
|
@ -12985,6 +12982,20 @@ Negative Passed: 3918/3918 (100.00%)
|
|||
· ───────────
|
||||
╰────
|
||||
|
||||
× The keyword 'yield' is reserved
|
||||
╭─[language/expressions/dynamic-import/import-assertions/2nd-param-yield-ident-invalid.js:18:1]
|
||||
18 │
|
||||
19 │ import('./empty_FIXTURE.js', yield);
|
||||
· ─────
|
||||
╰────
|
||||
|
||||
× The keyword 'yield' is reserved
|
||||
╭─[language/expressions/dynamic-import/import-attributes/2nd-param-yield-ident-invalid.js:18:1]
|
||||
18 │
|
||||
19 │ import('./empty_FIXTURE.js', yield);
|
||||
· ─────
|
||||
╰────
|
||||
|
||||
× Cannot assign to this expression
|
||||
╭─[language/expressions/dynamic-import/syntax/invalid/invalid-assignmenttargettype-syntax-error-1-update-expression.js:45:1]
|
||||
45 │
|
||||
|
|
@ -18593,6 +18604,27 @@ Negative Passed: 3918/3918 (100.00%)
|
|||
· ▲
|
||||
╰────
|
||||
|
||||
× Keywords cannot contain escape characters
|
||||
╭─[language/literals/boolean/false-with-unicode.js:19:1]
|
||||
19 │
|
||||
20 │ f\u{61}lse;
|
||||
· ──────────
|
||||
╰────
|
||||
|
||||
× Keywords cannot contain escape characters
|
||||
╭─[language/literals/boolean/true-with-unicode.js:19:1]
|
||||
19 │
|
||||
20 │ tru\u{65};
|
||||
· ─────────
|
||||
╰────
|
||||
|
||||
× Keywords cannot contain escape characters
|
||||
╭─[language/literals/null/null-with-unicode.js:19:1]
|
||||
19 │
|
||||
20 │ n\u{75}ll;
|
||||
· ─────────
|
||||
╰────
|
||||
|
||||
× '0'-prefixed octal literals and octal escape sequences are deprecated
|
||||
╭─[language/literals/numeric/7.8.3-1gs.js:14:1]
|
||||
14 │
|
||||
|
|
@ -19320,45 +19352,6 @@ Negative Passed: 3918/3918 (100.00%)
|
|||
· ─
|
||||
╰────
|
||||
|
||||
× Identifier `test262_a` has already been declared
|
||||
╭─[language/module-code/early-dup-assert-key-export.js:20:1]
|
||||
20 │ export * from './import-assertion-3_FIXTURE.js' assert {
|
||||
21 │ test262_a: '',
|
||||
· ────┬────
|
||||
· ╰── `test262_a` has already been declared here
|
||||
22 │ test262_b: '',
|
||||
23 │ 'test262_\u0061': ''
|
||||
· ────────┬───────
|
||||
· ╰── It can not be redeclared here
|
||||
24 │ };
|
||||
╰────
|
||||
|
||||
× Identifier `test262_a` has already been declared
|
||||
╭─[language/module-code/early-dup-assert-key-import-nobinding.js:21:1]
|
||||
21 │ import './import-assertion-2_FIXTURE.js' assert {
|
||||
22 │ test262_a: '',
|
||||
· ────┬────
|
||||
· ╰── `test262_a` has already been declared here
|
||||
23 │ test262_b: '',
|
||||
24 │ 'test262_\u0061': ''
|
||||
· ────────┬───────
|
||||
· ╰── It can not be redeclared here
|
||||
25 │ };
|
||||
╰────
|
||||
|
||||
× Identifier `test262_a` has already been declared
|
||||
╭─[language/module-code/early-dup-assert-key-import-withbinding.js:21:1]
|
||||
21 │ import x from './import-assertion-1_FIXTURE.js' assert {
|
||||
22 │ test262_a: '',
|
||||
· ────┬────
|
||||
· ╰── `test262_a` has already been declared here
|
||||
23 │ test262_b: '',
|
||||
24 │ 'test262_\u0061': ''
|
||||
· ────────┬───────
|
||||
· ╰── It can not be redeclared here
|
||||
25 │ };
|
||||
╰────
|
||||
|
||||
× Duplicated export 'z'
|
||||
╭─[language/module-code/early-dup-export-as-star-as.js:17:1]
|
||||
17 │ var x;
|
||||
|
|
@ -19739,6 +19732,78 @@ Negative Passed: 3918/3918 (100.00%)
|
|||
22 │
|
||||
╰────
|
||||
|
||||
× Identifier `type` has already been declared
|
||||
╭─[language/module-code/import-assertions/early-dup-assert-key-export.js:20:1]
|
||||
20 │ export * from './import-assertion-3_FIXTURE.js' assert {
|
||||
21 │ type: 'json',
|
||||
· ──┬─
|
||||
· ╰── `type` has already been declared here
|
||||
22 │ 'typ\u0065': ''
|
||||
· ─────┬─────
|
||||
· ╰── It can not be redeclared here
|
||||
23 │ };
|
||||
╰────
|
||||
|
||||
× Identifier `type` has already been declared
|
||||
╭─[language/module-code/import-assertions/early-dup-assert-key-import-nobinding.js:21:1]
|
||||
21 │ import './import-assertion-2_FIXTURE.js' assert {
|
||||
22 │ type: 'json',
|
||||
· ──┬─
|
||||
· ╰── `type` has already been declared here
|
||||
23 │ 'typ\u0065': ''
|
||||
· ─────┬─────
|
||||
· ╰── It can not be redeclared here
|
||||
24 │ };
|
||||
╰────
|
||||
|
||||
× Identifier `type` has already been declared
|
||||
╭─[language/module-code/import-assertions/early-dup-assert-key-import-withbinding.js:21:1]
|
||||
21 │ import x from './import-assertion-1_FIXTURE.js' assert {
|
||||
22 │ type: 'json',
|
||||
· ──┬─
|
||||
· ╰── `type` has already been declared here
|
||||
23 │ 'typ\u0065': ''
|
||||
· ─────┬─────
|
||||
· ╰── It can not be redeclared here
|
||||
24 │ };
|
||||
╰────
|
||||
|
||||
× Identifier `type` has already been declared
|
||||
╭─[language/module-code/import-attributes/early-dup-attribute-key-export.js:20:1]
|
||||
20 │ export * from './import-attribute-3_FIXTURE.js' with {
|
||||
21 │ type: 'json',
|
||||
· ──┬─
|
||||
· ╰── `type` has already been declared here
|
||||
22 │ 'typ\u0065': ''
|
||||
· ─────┬─────
|
||||
· ╰── It can not be redeclared here
|
||||
23 │ };
|
||||
╰────
|
||||
|
||||
× Identifier `type` has already been declared
|
||||
╭─[language/module-code/import-attributes/early-dup-attribute-key-import-nobinding.js:21:1]
|
||||
21 │ import './import-attribute-2_FIXTURE.js' with {
|
||||
22 │ type: 'json',
|
||||
· ──┬─
|
||||
· ╰── `type` has already been declared here
|
||||
23 │ 'typ\u0065': ''
|
||||
· ─────┬─────
|
||||
· ╰── It can not be redeclared here
|
||||
24 │ };
|
||||
╰────
|
||||
|
||||
× Identifier `type` has already been declared
|
||||
╭─[language/module-code/import-attributes/early-dup-attribute-key-import-withbinding.js:21:1]
|
||||
21 │ import x from './import-attribute-1_FIXTURE.js' with {
|
||||
22 │ type: 'json',
|
||||
· ──┬─
|
||||
· ╰── `type` has already been declared here
|
||||
23 │ 'typ\u0065': ''
|
||||
· ─────┬─────
|
||||
· ╰── It can not be redeclared here
|
||||
24 │ };
|
||||
╰────
|
||||
|
||||
× Private identifier '#x' is not allowed outside class bodies
|
||||
╭─[language/module-code/invalid-private-names-call-expression-bad-reference.js:39:1]
|
||||
39 │
|
||||
|
|
|
|||
|
|
@ -15803,28 +15803,23 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts"
|
|||
1 │ import * as f from "./first" assert {
|
||||
╰────
|
||||
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
× Expected `{` but found `EOF`
|
||||
╭─[conformance/importAttributes/importAttributes4.ts:1:1]
|
||||
1 │ import * as f from "./first" with
|
||||
· ─
|
||||
╰────
|
||||
help: Try insert a semicolon here
|
||||
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
× Expected `}` but found `EOF`
|
||||
╭─[conformance/importAttributes/importAttributes5.ts:1:1]
|
||||
1 │ import * as f from "./first" with {
|
||||
· ─
|
||||
╰────
|
||||
help: Try insert a semicolon here
|
||||
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
× Unexpected token
|
||||
╭─[conformance/importAttributes/importAttributes6.ts:2:1]
|
||||
2 │ // @filename: mod.mts
|
||||
3 │ import * as thing1 from "./mod.mjs" with { field: 0 };
|
||||
· ─
|
||||
· ─
|
||||
4 │ import * as thing2 from "./mod.mjs" with { field: `a` };
|
||||
╰────
|
||||
help: Try insert a semicolon here
|
||||
|
||||
× The keyword 'interface' is reserved
|
||||
╭─[conformance/interfaces/interfaceDeclarations/asiPreventsParsingAsInterface05.ts:2:1]
|
||||
|
|
|
|||
|
|
@ -86,6 +86,8 @@ impl<T: Case> Suite<T> for Test262Suite<T> {
|
|||
|
||||
fn skip_test_path(&self, path: &Path) -> bool {
|
||||
let path = path.to_string_lossy();
|
||||
// ignore markdown files
|
||||
path.ends_with(".md") ||
|
||||
// ignore fixtures
|
||||
path.contains("_FIXTURE") ||
|
||||
// ignore regexp as we don't have a regexp parser for now
|
||||
|
|
@ -159,8 +161,6 @@ impl Case for Test262Case {
|
|||
// Regex parser is required. See https://github.com/oxc-project/oxc/issues/385#issuecomment-1755566240
|
||||
"regexp-v-flag",
|
||||
"regexp-unicode-property-escapes",
|
||||
// Stage 3 `https://github.com/tc39/proposal-json-modules`
|
||||
"json-modules",
|
||||
// Stage 3 `https://github.com/tc39/proposal-decorators`
|
||||
"decorators",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit c4642dd714175b5d27939c920abc6059c9fddb06
|
||||
Subproject commit 2060494f280ba89d71a0f51d4ff171bafe476a05
|
||||
Loading…
Reference in a new issue