From ae2d352c8cf14b71fb36a63741e393f0bfad3451 Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Tue, 17 May 2016 18:44:41 +0300 Subject: [PATCH 1/5] Add proposal "Use JavaScript modules from Kotlin" --- README.md | 1 + proposals/js/consume-js-modules.md | 274 +++++++++++++++++++++++++++++ 2 files changed, 275 insertions(+) create mode 100644 proposals/js/consume-js-modules.md diff --git a/README.md b/README.md index c4c3c518e..ff1b3802d 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ The proposals themselves are colloquially referred to as KEEPs. | Coroutines | repo: [kotlin-coroutines](https://github.com/Kotlin/kotlin-coroutines) | [Issues](https://github.com/Kotlin/kotlin-coroutines/issues) | Type aliases | [type-aliases.md](proposals/type-aliases.md) | [Issue #4](https://github.com/Kotlin/KEEP/issues/4) | Bound callable references | [bound-callable-references.md](proposals/bound-callable-references.md) | [Issue #5](https://github.com/Kotlin/KEEP/issues/5) +| Use JavaScript modules from Kotlin | [js/consume-js-modules.md](proposals/js/consume-js-modules.md) | [PR #TODO](https://github.com/Kotlin/KEEP/pull/TODO) ## How to give feedback diff --git a/proposals/js/consume-js-modules.md b/proposals/js/consume-js-modules.md new file mode 100644 index 000000000..18256555d --- /dev/null +++ b/proposals/js/consume-js-modules.md @@ -0,0 +1,274 @@ +# Use JavaScript modules from Kotlin + +* **Type**: Kotlin JS design proposal +* **Author**: Zalim Bashorov +* **Contributors**: Andrey Breslav, Alexey Andreev +* **Status**: Submitted +* **Prototype**: + +## Goal + +Provide the way to add information about related JS module when write native declarations in Kotlin +and use this information to generate dependencies. + +## Intro + +In general case JS module can be: + - object with a bunch of declarations (in terms of kotlin it can be package or object) + - class + - function + - variable + +Additionally we should keep in mind that module import string should not be valid identifier. + + +## Proposed solution + +Propose to add: + +```kotlin +@Repeatable +@Retention(AnnotationRetention.BINARY) +@Target( + AnnotationTarget.CLASS, + AnnotationTarget.PROPERTY, + AnnotationTarget.FUNCTION, + AnnotationTarget.FILE) +annotation class JsModule( + val import: String, + vararg val kind: JsModuleKind = arrayOf(JsModuleKind.AMD, JsModuleKind.COMMON_JS, JsModuleKind.UMD) +) + +enum class JsModuleKind { + SIMPLE, + AMD, + COMMON_JS, // CJS? + UMD +} +``` + +It should be **repeatable** to allow to specialize settings for concrete JsModuleKind. + +It should have **binary retention** to be available from binary data. + +Annotation **allowed on classes, properties and functions** according to what can be exported from JS modules. + +In JS world objects often used as namespace/package so will be nice to allow to map such objects to Kotlin packages. + +Why just not using objects in all cases? +1. Some IDE features works better with package level declarations (e.g. auto import) +2. Accessing to package must not have side effects, so in some cases we can generate better code (alternative solution is add marker annotation) + +So to achieve that files allowed as target of the annotation. +But it's not enough, additionally we should provide the way to specify custom qualifier for native declarations in the file. + +It can be achieved by adding yet another parameter to the annotation or add a new annotation, like: + +```kotlin +@Retention(AnnotationRetention.BINARY) +@Target(AnnotationTarget.FILE) +annotation class JsPackage(val path: String) +``` +[TODO] think up a better name. + +The new annotation can be reused in case when we want to have package with long path in Kotlin, +but don't want to have long path in generated JS, e.g. for public API. +Of course, this problem can be fixed by adding another file "facade" with short qualifier. + + +Parameters: +- `import` -- string which will be used to import related module. +- `kind` -- shows for which kind of modules this role should be applied. + + +## Use cases (based on TypeScript declarations) + +**Simple module declaration** + +Code in TypeScript: +```typescript +declare module "MyExternalModule" { + export function foo(); + export var bar; + export namespace baz { + function boo(); + } +} +``` + +In Koltin it can be written like: +```kotlin +// file1.kt +@file:JsModule("MyExternalModule") +package MyExternalModule + +@native fun foo() {} +@native var bar: Any = noImpl; +``` +```kotlin +// file2.kt +@file:JsModule("MyExternalModule") +@file:JsPackage("baz") +package MyExternalModule.baz + +@native fun boo() {} +``` + +**Export by assignment** + +In TypeScript: +```typescript +declare module "MyExternalModule" { + export = function foo(); +} +``` + +In Kotlin: +```kotlin +package MyExternalModule + +@JsModule("MyExternalModule") +@native fun foo() {} +``` + +**Export by assignment form toplevel** + +In TypeScript: +```typescript +declare var prop: MyClass; +export = prop; +``` + +In Kotlin: +```kotlin +package SomeModule + +@JsModule("SomeModule") +@native var prop: MyClass = noImpl +``` + +**Export by assignment the declaration decelerated outside of module** + +In TypeScript: +```typescript +declare var prop: MyClass; + +declare module "MyExternalModule" { + export = prop; +} +``` + +In Kotlin: +```kotlin +package SomeModule + +@JsModule("MyExternalModule") +@JsModule("MyExternalModule", kind = JsModuleKind.SIMPLE) +@native var prop: MyClass = noImpl +``` +Second role means that when translate with SIMPLE module kind for this module +compiler should generate import through variable `MyExternalModule` + + +Another way: +```kotlin +package SomeModule + +@JsModule("MyExternalModule") +@JsModule("this", kind = JsModuleKind.SIMPLE) +@native var prop: MyClass = noImpl +``` +And now when translate with SIMPLE module kind for this module +compiler should generate import through variable `this` (usually it's Global Object) + + +## Implementation details + +### Backend + +Let's consider the following files: + +**declarations1.kt** +```kotlin +@file:JsModule("first-module") + +var a: Int = noImpl +``` + +**declarations2.kt** +```kotlin +@JsModule("second-module") +var b: Int = noImpl +``` + +**declarations3.kt** +```kotlin +@file: JsModule("thirdModule") + +var c: Int = noImpl +``` + +**declarations4.kt** +```kotlin +@JsModule("fourthModule") +var d: Int = noImpl +``` + +**usage.kt** +```kotlin +fun test() { + println(a) + println(b) + println(c) + println(d) +} +``` + +Use import value as is to declare dependencies when translate them with **any module kind except SIMPLE**. +
E.g. for CommonJS generate following: +```javascript +var first_module = require("first-module"); +var b = require("second-module"); +var thirdModule = require("thirdModule"); +var d = require("fourthModule"); +//... +``` + +When **module kind is SIMPLE** +- If import value is valid JS identifier or `this` then use it as is as name of identifier; +- Otherwise, try to get from `this` using import value as is (as string). + +```javascript +(function(first_module, b, thirdModule, d) { +// ... +}(this["first-module"], this["second-module"], thirdModule, fourthModule)); +``` + +### Frontend +- Report error when try to use native declaration which has `JsModule` annotations, but no one specify rule for current module kind. +- Prohibit to have many annotations which explicitly provide the same module kind. +- Prohibit to apply `JsModule` annotation to non-native declarations, except files.
+It can be relaxed later e.g. to reuse this annotation to allow translate a file to separate JS module, it can be useful to interop with some frameworks (see [KT-12093](https://youtrack.jetbrains.com/issue/KT-12093)) + +### IDE +- Add inspection for case when some declarations with the same fq-name have different JsModule annotations? + Consider next cases: + - function overloads + - package and functions + +## Open questions +1. Can we introduce default value for `import` parameter of `JsModule` and use the name of declaration as import string when argument not provided?
+If so, how it should work when the annotation used on file? + +2. What should be default value of `kind` parameter of `JsModule`? + 1. all kinds + 2. all kinds except SIMPLE + + In TypeScript (external) modules can be used only when compiler ran with module kind (in our terms it's all except SIMPLE). + So, should second be default to generate simpler code from TS declarations? + +3. Actually right now we needs to know only is `kind === SIMPLE` or not, so should we simplify API? + +4. Unfortunately we can't use constants for `kind` parameter to make API better. Can we fix it somehow? + +5. Will be nice to have the way to say that all declarations in this file is native. But how it fit with idea to replace `@native` with `external`? From ac8d5a6e960284c018a5166ec87364db100d580a Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Wed, 18 May 2016 10:52:27 +0300 Subject: [PATCH 2/5] README.md: update link to related PR --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ff1b3802d..88d35a5cd 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ The proposals themselves are colloquially referred to as KEEPs. | Coroutines | repo: [kotlin-coroutines](https://github.com/Kotlin/kotlin-coroutines) | [Issues](https://github.com/Kotlin/kotlin-coroutines/issues) | Type aliases | [type-aliases.md](proposals/type-aliases.md) | [Issue #4](https://github.com/Kotlin/KEEP/issues/4) | Bound callable references | [bound-callable-references.md](proposals/bound-callable-references.md) | [Issue #5](https://github.com/Kotlin/KEEP/issues/5) -| Use JavaScript modules from Kotlin | [js/consume-js-modules.md](proposals/js/consume-js-modules.md) | [PR #TODO](https://github.com/Kotlin/KEEP/pull/TODO) +| Use JavaScript modules from Kotlin | [js/consume-js-modules.md](proposals/js/consume-js-modules.md) | [PR #10](https://github.com/Kotlin/KEEP/pull/10) ## How to give feedback From ef5d38ad6ca7b87ded70ce078ca2f084a401d58c Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Wed, 1 Jun 2016 20:49:20 +0300 Subject: [PATCH 3/5] JS modules: prohibit to use the annotation on `var`; minor fixes in text. --- proposals/js/consume-js-modules.md | 42 ++++++++++++++++++------------ 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/proposals/js/consume-js-modules.md b/proposals/js/consume-js-modules.md index 18256555d..1f74de93e 100644 --- a/proposals/js/consume-js-modules.md +++ b/proposals/js/consume-js-modules.md @@ -4,7 +4,6 @@ * **Author**: Zalim Bashorov * **Contributors**: Andrey Breslav, Alexey Andreev * **Status**: Submitted -* **Prototype**: ## Goal @@ -14,12 +13,12 @@ and use this information to generate dependencies. ## Intro In general case JS module can be: - - object with a bunch of declarations (in terms of kotlin it can be package or object) + - object with a bunch of declarations (in terms of Kotlin it can be package or object) - class - function - variable -Additionally we should keep in mind that module import string should not be valid identifier. +Additionally, we should keep in mind that module import string should not be a valid identifier. ## Proposed solution @@ -36,7 +35,7 @@ Propose to add: AnnotationTarget.FILE) annotation class JsModule( val import: String, - vararg val kind: JsModuleKind = arrayOf(JsModuleKind.AMD, JsModuleKind.COMMON_JS, JsModuleKind.UMD) + vararg val kind: JsModuleKind = ... // arrayOf(JsModuleKind.AMD, JsModuleKind.COMMON_JS, JsModuleKind.UMD) ) enum class JsModuleKind { @@ -53,14 +52,21 @@ It should have **binary retention** to be available from binary data. Annotation **allowed on classes, properties and functions** according to what can be exported from JS modules. +_**Frontend:** prohibit to use the annotation on `var`_ +
+_It doesn't make sense on module systems except CommonJS (node.js) +and to use it with CommonJS we should call `require` on every call sites to get the current value._ +
+_Since it very rare case propose to prohibit it now._ + In JS world objects often used as namespace/package so will be nice to allow to map such objects to Kotlin packages. Why just not using objects in all cases? 1. Some IDE features works better with package level declarations (e.g. auto import) 2. Accessing to package must not have side effects, so in some cases we can generate better code (alternative solution is add marker annotation) -So to achieve that files allowed as target of the annotation. -But it's not enough, additionally we should provide the way to specify custom qualifier for native declarations in the file. +So to achieve that files allowed as the target of the annotation. +But it's not enough, additionally, we should provide the way to specify custom qualifier for native declarations in the file. It can be achieved by adding yet another parameter to the annotation or add a new annotation, like: @@ -72,9 +78,10 @@ annotation class JsPackage(val path: String) [TODO] think up a better name. The new annotation can be reused in case when we want to have package with long path in Kotlin, -but don't want to have long path in generated JS, e.g. for public API. -Of course, this problem can be fixed by adding another file "facade" with short qualifier. +but don't want to have a long path in generated JS, e.g. for public API. +Of course, this problem can be fixed by adding another file "facade" with a short qualifier. +// it can't part of JsModule becouse will be reused for native declarations inside nested namespaces/packages Parameters: - `import` -- string which will be used to import related module. @@ -131,7 +138,7 @@ package MyExternalModule @native fun foo() {} ``` -**Export by assignment form toplevel** +**Export by assignment from toplevel** In TypeScript: ```typescript @@ -245,13 +252,14 @@ When **module kind is SIMPLE** ``` ### Frontend -- Report error when try to use native declaration which has `JsModule` annotations, but no one specify rule for current module kind. +- Report error when try to use native declaration which has `JsModule` annotations, but no one specifies a rule for current module kind. - Prohibit to have many annotations which explicitly provide the same module kind. - Prohibit to apply `JsModule` annotation to non-native declarations, except files.
-It can be relaxed later e.g. to reuse this annotation to allow translate a file to separate JS module, it can be useful to interop with some frameworks (see [KT-12093](https://youtrack.jetbrains.com/issue/KT-12093)) +It can be relaxed later e.g. to reuse this annotation to allow translate a file to separate JS module, +it can be useful to interop with some frameworks (see [KT-12093](https://youtrack.jetbrains.com/issue/KT-12093)) ### IDE -- Add inspection for case when some declarations with the same fq-name have different JsModule annotations? +- Add inspection for the case when some declarations with the same fq- Consider next cases: - function overloads - package and functions @@ -260,15 +268,15 @@ It can be relaxed later e.g. to reuse this annotation to allow translate a file 1. Can we introduce default value for `import` parameter of `JsModule` and use the name of declaration as import string when argument not provided?
If so, how it should work when the annotation used on file? -2. What should be default value of `kind` parameter of `JsModule`? +2. What should be used as default value of `kind` parameter of `JsModule`? 1. all kinds 2. all kinds except SIMPLE In TypeScript (external) modules can be used only when compiler ran with module kind (in our terms it's all except SIMPLE). - So, should second be default to generate simpler code from TS declarations? + So, should the second be default to generate simpler code from TS declarations? -3. Actually right now we needs to know only is `kind === SIMPLE` or not, so should we simplify API? +3. Actually, right now we needs to know only is `kind === SIMPLE` or not, so should we simplify API? -4. Unfortunately we can't use constants for `kind` parameter to make API better. Can we fix it somehow? +4. Unfortunately, we can't use constants for `kind` parameter to make API better. Can we fix it somehow? -5. Will be nice to have the way to say that all declarations in this file is native. But how it fit with idea to replace `@native` with `external`? +5. Will be nice to have the way to say that all declarations in this file are native. But how it fit with the idea to replace `@native` with `external`? From af35c13a61ef663f05abbc8aeace54afa0e95df6 Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Wed, 1 Jun 2016 22:35:33 +0300 Subject: [PATCH 4/5] JS modules: rethink how mark declarations which available with module systems and without module systems --- proposals/js/consume-js-modules.md | 102 ++++++++++++++--------------- 1 file changed, 49 insertions(+), 53 deletions(-) diff --git a/proposals/js/consume-js-modules.md b/proposals/js/consume-js-modules.md index 1f74de93e..934aeef0e 100644 --- a/proposals/js/consume-js-modules.md +++ b/proposals/js/consume-js-modules.md @@ -26,28 +26,15 @@ Additionally, we should keep in mind that module import string should not be a v Propose to add: ```kotlin -@Repeatable @Retention(AnnotationRetention.BINARY) @Target( AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.FILE) -annotation class JsModule( - val import: String, - vararg val kind: JsModuleKind = ... // arrayOf(JsModuleKind.AMD, JsModuleKind.COMMON_JS, JsModuleKind.UMD) -) - -enum class JsModuleKind { - SIMPLE, - AMD, - COMMON_JS, // CJS? - UMD -} +annotation class JsModule(val import: String) ``` -It should be **repeatable** to allow to specialize settings for concrete JsModuleKind. - It should have **binary retention** to be available from binary data. Annotation **allowed on classes, properties and functions** according to what can be exported from JS modules. @@ -77,16 +64,51 @@ annotation class JsPackage(val path: String) ``` [TODO] think up a better name. -The new annotation can be reused in case when we want to have package with long path in Kotlin, -but don't want to have a long path in generated JS, e.g. for public API. +The new annotation prefered because it can be reused in two cases: +1. _Major:_ for native declarations inside nested namespaces / packages. +2. _Minor:_ when we want to have package with long path in Kotlin, +but don't want to have a long path in generated JS, e.g. for JS public API. Of course, this problem can be fixed by adding another file "facade" with a short qualifier. -// it can't part of JsModule becouse will be reused for native declarations inside nested namespaces/packages -Parameters: +**Parameter of `JsModule` annotation:** - `import` -- string which will be used to import related module. -- `kind` -- shows for which kind of modules this role should be applied. +## How mark declarations which available with module systems and without module systems? + +Possible solutions: +1. Add additional parameter to `JsModule` annotation +Pros: +- minor: simpler to discover +Cons: +- it makes harder to reuse `JsModule` annotation for non-external declaration in the future +- maybe it'll strange to see a parameter about declaration in the annotation about module + +2. Add separate annotation +Pros: +- minor: simpler to evolve +Cons: +- more verbose +- yet another annotation +Note: +- the annotation allowed only on native declarations +- the annotation w/o `JsModule` doesn't change anything + +Another problem is to think up a good name for that. + +Some name candidates: +- JsSimpleModule +- JsNonModule +- JsPlainModule +- JsPlainModule +- JsPlain +- existsInNonModuleMode +- availableInNonModuleMode +- existsOutsideOfModule +- availableOutsideOfModule + + +**NOTE: In following code fragments temporary used `JsNonModule` annotation.** ## Use cases (based on TypeScript declarations) @@ -170,24 +192,9 @@ In Kotlin: package SomeModule @JsModule("MyExternalModule") -@JsModule("MyExternalModule", kind = JsModuleKind.SIMPLE) -@native var prop: MyClass = noImpl -``` -Second role means that when translate with SIMPLE module kind for this module -compiler should generate import through variable `MyExternalModule` - - -Another way: -```kotlin -package SomeModule - -@JsModule("MyExternalModule") -@JsModule("this", kind = JsModuleKind.SIMPLE) +@JsNonModule @native var prop: MyClass = noImpl ``` -And now when translate with SIMPLE module kind for this module -compiler should generate import through variable `this` (usually it's Global Object) - ## Implementation details @@ -231,7 +238,7 @@ fun test() { } ``` -Use import value as is to declare dependencies when translate them with **any module kind except SIMPLE**. +Use import value as is to declare dependencies when translate them with **module kind COMMON_JS or AMD**.
E.g. for CommonJS generate following: ```javascript var first_module = require("first-module"); @@ -242,24 +249,26 @@ var d = require("fourthModule"); ``` When **module kind is SIMPLE** -- If import value is valid JS identifier or `this` then use it as is as name of identifier; +- If import value is valid JS identifier use it as is as name of identifier; - Otherwise, try to get from `this` using import value as is (as string). ```javascript (function(first_module, b, thirdModule, d) { +// ... + println(first_module.a, b, thirdModule.c, d); // ... }(this["first-module"], this["second-module"], thirdModule, fourthModule)); ``` +And for **module kind is UMD** compiler should use relevant rule for each block. + ### Frontend -- Report error when try to use native declaration which has `JsModule` annotations, but no one specifies a rule for current module kind. -- Prohibit to have many annotations which explicitly provide the same module kind. - Prohibit to apply `JsModule` annotation to non-native declarations, except files.
It can be relaxed later e.g. to reuse this annotation to allow translate a file to separate JS module, it can be useful to interop with some frameworks (see [KT-12093](https://youtrack.jetbrains.com/issue/KT-12093)) ### IDE -- Add inspection for the case when some declarations with the same fq- +- Add inspection for the case when some declarations with the same fq-name Consider next cases: - function overloads - package and functions @@ -267,16 +276,3 @@ it can be useful to interop with some frameworks (see [KT-12093](https://youtrac ## Open questions 1. Can we introduce default value for `import` parameter of `JsModule` and use the name of declaration as import string when argument not provided?
If so, how it should work when the annotation used on file? - -2. What should be used as default value of `kind` parameter of `JsModule`? - 1. all kinds - 2. all kinds except SIMPLE - - In TypeScript (external) modules can be used only when compiler ran with module kind (in our terms it's all except SIMPLE). - So, should the second be default to generate simpler code from TS declarations? - -3. Actually, right now we needs to know only is `kind === SIMPLE` or not, so should we simplify API? - -4. Unfortunately, we can't use constants for `kind` parameter to make API better. Can we fix it somehow? - -5. Will be nice to have the way to say that all declarations in this file are native. But how it fit with the idea to replace `@native` with `external`? From a52e37e274489eb563b0ab7e482b7ab249dad0d3 Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Tue, 28 Jun 2016 19:37:19 +0300 Subject: [PATCH 5/5] JsModules: updated after the meeting; added some thoughts and the alternative solution. --- proposals/js/consume-js-modules.md | 122 ++++++++++++----------------- 1 file changed, 49 insertions(+), 73 deletions(-) diff --git a/proposals/js/consume-js-modules.md b/proposals/js/consume-js-modules.md index 934aeef0e..dfdd7ef93 100644 --- a/proposals/js/consume-js-modules.md +++ b/proposals/js/consume-js-modules.md @@ -60,9 +60,12 @@ It can be achieved by adding yet another parameter to the annotation or add a ne ```kotlin @Retention(AnnotationRetention.BINARY) @Target(AnnotationTarget.FILE) -annotation class JsPackage(val path: String) +annotation class JsQualifier(val value: String) ``` -[TODO] think up a better name. + +Other possible names: +* JsPackage(val qualifier: String) +* JsPackagePrefix(val value: String) The new annotation prefered because it can be reused in two cases: 1. _Major:_ for native declarations inside nested namespaces / packages. @@ -80,8 +83,6 @@ Possible solutions: 1. Add additional parameter to `JsModule` annotation Pros: - minor: simpler to discover -Cons: -- it makes harder to reuse `JsModule` annotation for non-external declaration in the future - maybe it'll strange to see a parameter about declaration in the annotation about module 2. Add separate annotation @@ -94,9 +95,16 @@ Note: - the annotation allowed only on native declarations - the annotation w/o `JsModule` doesn't change anything +3. JsModule + native +Pros: +- simple, no extra annotation (?) +Cons: +- prevents the replacement of `@native` by `external` + Another problem is to think up a good name for that. Some name candidates: +- JsPlainAccess (Calls) - JsSimpleModule - JsNonModule - JsPlainModule @@ -107,8 +115,7 @@ Some name candidates: - existsOutsideOfModule - availableOutsideOfModule - -**NOTE: In following code fragments temporary used `JsNonModule` annotation.** +_**Decision:** use `JsNonModule` annotations in prototype_ ## Use cases (based on TypeScript declarations) @@ -196,83 +203,52 @@ package SomeModule @native var prop: MyClass = noImpl ``` -## Implementation details +## Frontend +- Native declarations w/o both annotations (`JsNonModule`, `JsNonModule`) available when compile with any module kind. +- Prohibit to use native declarations annotated by `JsModule` and not annotated by `JsNonModule` when compile with module kind == PLAIN. +- Prohibit to use native declarations annotated by `JsNonModule` and not annotated by `JsModule` when compile with module kind != PLAIN. +- Prohibit to use declarations annotated by only `JsNonModule` or `JsNonModule` when compile with module kind == UMD. +- Prohibit to apply `JsModule` and `JsNonModule` annotation to non-native declarations, except files. -### Backend -Let's consider the following files: +## Open questions +1. Can we introduce default value for `import` parameter of `JsModule` and use the name of declaration as import string when argument not provided?
+If so, how it should work when the annotation used on file? -**declarations1.kt** -```kotlin -@file:JsModule("first-module") +_**Decision:** not now_ -var a: Int = noImpl -``` +2. How import/export modules in non module kinds when import string is not valid identifier (it's possible for IDEA modules)? +
+Right now we valid identifiers use as is and generate something like `this["module-name"]` otherwise. But: +- it looks asymmetric; +- nobody use `this["module-name"]`. -**declarations2.kt** -```kotlin -@JsModule("second-module") -var b: Int = noImpl -``` +Possible solutions: +- always use `sanitized_name`; +- always use `this[module-name]`; +- always use both. -**declarations3.kt** -```kotlin -@file: JsModule("thirdModule") +_**Propose:** decide it later._ -var c: Int = noImpl -``` -**declarations4.kt** -```kotlin -@JsModule("fourthModule") -var d: Int = noImpl -``` +## Alternative solution +Use the one annotation instead of two: -**usage.kt** ```kotlin -fun test() { - println(a) - println(b) - println(c) - println(d) -} -``` - -Use import value as is to declare dependencies when translate them with **module kind COMMON_JS or AMD**. -
E.g. for CommonJS generate following: -```javascript -var first_module = require("first-module"); -var b = require("second-module"); -var thirdModule = require("thirdModule"); -var d = require("fourthModule"); -//... -``` - -When **module kind is SIMPLE** -- If import value is valid JS identifier use it as is as name of identifier; -- Otherwise, try to get from `this` using import value as is (as string). +@Retention(AnnotationRetention.BINARY) +@Target( + AnnotationTarget.CLASS, + AnnotationTarget.PROPERTY, + AnnotationTarget.FUNCTION, + AnnotationTarget.FILE) +@Repeatable +annotation class JsAccessible(from: ModuleType, import: String) -```javascript -(function(first_module, b, thirdModule, d) { -// ... - println(first_module.a, b, thirdModule.c, d); -// ... -}(this["first-module"], this["second-module"], thirdModule, fourthModule)); +// alternative names: JsModule, Module +enum class ModuleType { + NONE, // NON_MODULE + SYSTEM // MODULE_SYSTEM +} ``` -And for **module kind is UMD** compiler should use relevant rule for each block. - -### Frontend -- Prohibit to apply `JsModule` annotation to non-native declarations, except files.
-It can be relaxed later e.g. to reuse this annotation to allow translate a file to separate JS module, -it can be useful to interop with some frameworks (see [KT-12093](https://youtrack.jetbrains.com/issue/KT-12093)) - -### IDE -- Add inspection for the case when some declarations with the same fq-name - Consider next cases: - - function overloads - - package and functions - -## Open questions -1. Can we introduce default value for `import` parameter of `JsModule` and use the name of declaration as import string when argument not provided?
-If so, how it should work when the annotation used on file? +So, current default PLAIN requires NON_MODULE type; AMD, CommonJS, SystemJS and ES2016 -- MODULE_SYSTEM; and UMD -- MODULE_SYSTEM & NON_MODULE