@cocreate/plugins 1.9.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,3 @@
1
1
  # These are supported funding model platforms
2
2
 
3
- github: CoCreate-app
3
+ github: CoCreateJS
@@ -12,16 +12,27 @@ jobs:
12
12
  - name: Checkout repository
13
13
  uses: actions/checkout@v7
14
14
 
15
- - name: Sync package.json description to GitHub About
15
+ - name: Sync package.json description and keywords to GitHub repository
16
16
  env:
17
- GITHUB_TOKEN: ${{ secrets.GITHUB }}
17
+ GH_TOKEN: ${{ secrets.GITHUB }}
18
18
  run: |
19
- DESC=$(jq -r '.description // empty' package.json)
20
- if [ -n "$DESC" ]; then
21
- echo "Updating repository description to: $DESC"
22
- gh api -X PATCH repos/${{ github.repository }} -f description="$DESC"
19
+ if [ ! -f package.json ]; then
20
+ echo "No package.json found; skipping repository metadata sync."
21
+ exit 0
22
+ fi
23
+
24
+ DESCRIPTION=$(jq -r '.description // empty' package.json)
25
+ if [ -n "$DESCRIPTION" ]; then
26
+ gh api --method PATCH "repos/${GITHUB_REPOSITORY}" -f description="$DESCRIPTION"
27
+ else
28
+ echo "No package description found; leaving GitHub description unchanged."
29
+ fi
30
+
31
+ if jq -e '.keywords | type == "array"' package.json >/dev/null; then
32
+ TOPICS=$(jq -c '.keywords | map(ascii_downcase | gsub("[^a-z0-9-]+"; "-") | gsub("(^-+|-+$)"; "") | .[0:50]) | map(select(length > 0)) | unique | .[0:20]' package.json)
33
+ printf '{"names":%s}' "$TOPICS" | gh api --method PUT -H "Accept: application/vnd.github+json" "repos/${GITHUB_REPOSITORY}/topics" --input -
23
34
  else
24
- echo "No description found in package.json."
35
+ echo "No package keywords found; leaving GitHub topics unchanged."
25
36
  fi
26
37
 
27
38
  release:
package/CHANGELOG.md CHANGED
@@ -1,115 +1,150 @@
1
- ## [1.4.2](https://github.com/CoCreate-app/CoCreate-plugins/compare/v1.4.1...v1.4.2) (2026-07-19)
1
+ # [2.0.0](https://github.com/CoCreateJS/plugins/compare/v1.4.2...v2.0.0) (2026-11-02)
2
2
 
3
3
 
4
4
  ### Bug Fixes
5
5
 
6
- * set default branch to main ([6252e49](https://github.com/CoCreate-app/CoCreate-plugins/commit/6252e49ac94bf14ba903943788cc443c5a5fd113))
6
+ * add Deno specific entries to .gitignore ([2e77744](https://github.com/CoCreateJS/plugins/commit/2e77744e33cab2968d7f9c2bf41f9dae38f25455))
7
+ * await pending plugin initialization ([d500cd2](https://github.com/CoCreateJS/plugins/commit/d500cd23245ddbbca0f31951dd8356888981ee4a))
8
+ * bump [@cocreate](https://github.com/cocreate) dependencies ([7893b9e](https://github.com/CoCreateJS/plugins/commit/7893b9ea09c130488553d33bdb4a7c6dd71e68f0))
9
+ * bump versions ([e70fdfa](https://github.com/CoCreateJS/plugins/commit/e70fdfadbbebb13880dc1cd2c40d5ade30092f37))
10
+ * bump versions ([321063f](https://github.com/CoCreateJS/plugins/commit/321063f9c223b5107629dcf516ea18380e0a5ad5))
11
+ * execute plugin configurations directly through AST ([f507b05](https://github.com/CoCreateJS/plugins/commit/f507b05fa7a831f9a7686067f1eaf506543b29e9))
12
+ * improve dynamic plugin initialization ([f834366](https://github.com/CoCreateJS/plugins/commit/f834366dda96db9aaf678780f14edbca159f6fb4))
13
+ * prevent duplicate plugin initialization ([7e36818](https://github.com/CoCreateJS/plugins/commit/7e368188c7cfd8804b7401cca609989dcfe99d2e))
14
+ * remove trailing slash from node_modules entry in .gitignore ([77ec08a](https://github.com/CoCreateJS/plugins/commit/77ec08a37696cba91aea9fcde55a1bd0e644e02c))
15
+ * stabilize dynamic plugin initialization ([4bd1763](https://github.com/CoCreateJS/plugins/commit/4bd176386f1e18a5ae865b7df1c19f43c5948b24))
16
+ * update git url ([06fdd2d](https://github.com/CoCreateJS/plugins/commit/06fdd2d13142a4614e0f983c0248753df2f0250d))
17
+ * update GitHub Actions to use latest versions and sync package.json description ([bf390e2](https://github.com/CoCreateJS/plugins/commit/bf390e24d9dbcb1738358fa6beba2d0546ce94ac))
18
+ * update string escape for plugin name checks ([2d99094](https://github.com/CoCreateJS/plugins/commit/2d9909486f23a6b6e38efbd107faa475893c03a4))
7
19
 
8
- ## [1.4.1](https://github.com/CoCreate-app/CoCreate-plugins/compare/v1.4.0...v1.4.1) (2026-07-17)
20
+
21
+ ### chore
22
+
23
+ * record package identity migration as breaking ([9926af9](https://github.com/CoCreateJS/plugins/commit/9926af94d90d5330def0245d22348f6616d31188))
24
+
25
+
26
+ ### Features
27
+
28
+ * execute plugin configs through AST ([4e4d3a5](https://github.com/CoCreateJS/plugins/commit/4e4d3a509ea742fcc6b4c62902760438fa9c220c))
29
+ * replace uuid import and usage with utils ([f92a5a3](https://github.com/CoCreateJS/plugins/commit/f92a5a33ad9bcdfde6fb75d56dac8f0ff7a6b679))
30
+
31
+
32
+ ### BREAKING CHANGES
33
+
34
+ * Consumers must install @cocreate/plugins and use the renamed CoCreateJS repository identity; the previous package and repository identities are no longer supported.
35
+
36
+ ## [1.4.2](https://github.com/CoCreateJS/CoCreate-plugins/compare/v1.4.1...v1.4.2) (2026-07-19)
37
+
38
+
39
+ ### Bug Fixes
40
+
41
+ * set default branch to main ([6252e49](https://github.com/CoCreateJS/CoCreate-plugins/commit/6252e49ac94bf14ba903943788cc443c5a5fd113))
42
+
43
+ ## [1.4.1](https://github.com/CoCreateJS/CoCreate-plugins/compare/v1.4.0...v1.4.1) (2026-07-17)
9
44
 
10
45
 
11
46
  ### Bug Fixes
12
47
 
13
- * bump dependencies ([0bcc639](https://github.com/CoCreate-app/CoCreate-plugins/commit/0bcc6392a3f033f26f9d8d1145e10a6df7de7ab2))
48
+ * bump dependencies ([0bcc639](https://github.com/CoCreateJS/CoCreate-plugins/commit/0bcc6392a3f033f26f9d8d1145e10a6df7de7ab2))
14
49
 
15
- # [1.4.0](https://github.com/CoCreate-app/CoCreate-plugins/compare/v1.3.1...v1.4.0) (2026-07-17)
50
+ # [1.4.0](https://github.com/CoCreateJS/CoCreate-plugins/compare/v1.3.1...v1.4.0) (2026-07-17)
16
51
 
17
52
 
18
53
  ### Features
19
54
 
20
- * enhance plugin initialization and resource preloading logic ([ee17bc1](https://github.com/CoCreate-app/CoCreate-plugins/commit/ee17bc12b47c09085cd2527ef18a6fce36f21123))
21
- * improve plugin processing and operator execution logic ([486faa8](https://github.com/CoCreate-app/CoCreate-plugins/commit/486faa81839a49d4958ccb75721b161102cc0168))
22
- * update automated workflow and release configuration for improved semantic release handling ([06e65c9](https://github.com/CoCreate-app/CoCreate-plugins/commit/06e65c9eddd29398f3bd824e295089722faf9fad))
55
+ * enhance plugin initialization and resource preloading logic ([ee17bc1](https://github.com/CoCreateJS/CoCreate-plugins/commit/ee17bc12b47c09085cd2527ef18a6fce36f21123))
56
+ * improve plugin processing and operator execution logic ([486faa8](https://github.com/CoCreateJS/CoCreate-plugins/commit/486faa81839a49d4958ccb75721b161102cc0168))
57
+ * update automated workflow and release configuration for improved semantic release handling ([06e65c9](https://github.com/CoCreateJS/CoCreate-plugins/commit/06e65c9eddd29398f3bd824e295089722faf9fad))
23
58
 
24
- ## [1.3.1](https://github.com/CoCreate-app/CoCreate-plugins/compare/v1.3.0...v1.3.1) (2026-07-11)
59
+ ## [1.3.1](https://github.com/CoCreateJS/CoCreate-plugins/compare/v1.3.0...v1.3.1) (2026-07-11)
25
60
 
26
61
 
27
62
  ### Bug Fixes
28
63
 
29
- * bump package versions ([3f992ea](https://github.com/CoCreate-app/CoCreate-plugins/commit/3f992ea145328efac344e4f470fe19a87d5209e9))
30
- * clean attribute name for method matching in processPlugin function ([7389d12](https://github.com/CoCreate-app/CoCreate-plugins/commit/7389d12bd6f4206c506456d64341ceef4b304313))
31
- * correct minor typos in code comments and variable names for clarity. ([7e43622](https://github.com/CoCreate-app/CoCreate-plugins/commit/7e4362289c9f51ac6673873802d5b58a7992638e))
32
- * demo plugin initialization syntax and remove redundant tests ([c126bc8](https://github.com/CoCreate-app/CoCreate-plugins/commit/c126bc87edfb9669ea6c8a79e13a0cd0c09a1bd3))
33
- * removed post install and add add packages to apprved list ([548be97](https://github.com/CoCreate-app/CoCreate-plugins/commit/548be977ba710af907ad8e95f7ebcd3a75462326))
34
- * semantic version handling ([7606d54](https://github.com/CoCreate-app/CoCreate-plugins/commit/7606d54cd447a3cf10c3f86d54bca1f0aa7fe1a1))
64
+ * bump package versions ([3f992ea](https://github.com/CoCreateJS/CoCreate-plugins/commit/3f992ea145328efac344e4f470fe19a87d5209e9))
65
+ * clean attribute name for method matching in processPlugin function ([7389d12](https://github.com/CoCreateJS/CoCreate-plugins/commit/7389d12bd6f4206c506456d64341ceef4b304313))
66
+ * correct minor typos in code comments and variable names for clarity. ([7e43622](https://github.com/CoCreateJS/CoCreate-plugins/commit/7e4362289c9f51ac6673873802d5b58a7992638e))
67
+ * demo plugin initialization syntax and remove redundant tests ([c126bc8](https://github.com/CoCreateJS/CoCreate-plugins/commit/c126bc87edfb9669ea6c8a79e13a0cd0c09a1bd3))
68
+ * removed post install and add add packages to apprved list ([548be97](https://github.com/CoCreateJS/CoCreate-plugins/commit/548be977ba710af907ad8e95f7ebcd3a75462326))
69
+ * semantic version handling ([7606d54](https://github.com/CoCreateJS/CoCreate-plugins/commit/7606d54cd447a3cf10c3f86d54bca1f0aa7fe1a1))
35
70
 
36
- # [1.3.0](https://github.com/CoCreate-app/CoCreate-plugins/compare/v1.2.1...v1.3.0) (2026-03-11)
71
+ # [1.3.0](https://github.com/CoCreateJS/CoCreate-plugins/compare/v1.2.1...v1.3.0) (2026-03-11)
37
72
 
38
73
 
39
74
  ### Bug Fixes
40
75
 
41
- * Update demo/index.html to test sequential execution of attributes as logic ([d71e0a5](https://github.com/CoCreate-app/CoCreate-plugins/commit/d71e0a562bc48337746583a04bf230f84134f6c7))
76
+ * Update demo/index.html to test sequential execution of attributes as logic ([d71e0a5](https://github.com/CoCreateJS/CoCreate-plugins/commit/d71e0a562bc48337746583a04bf230f84134f6c7))
42
77
 
43
78
 
44
79
  ### Features
45
80
 
46
- * Refactor plugin initialization to sequentially load resources and execute attributes in DOM order. Add support for direct $this references in attributes. Improve error handling and logging during plugin execution. ([08ecdb2](https://github.com/CoCreate-app/CoCreate-plugins/commit/08ecdb2b2bb8bba2cf3950eb2ef5e7ac77916203))
81
+ * Refactor plugin initialization to sequentially load resources and execute attributes in DOM order. Add support for direct $this references in attributes. Improve error handling and logging during plugin execution. ([08ecdb2](https://github.com/CoCreateJS/CoCreate-plugins/commit/08ecdb2b2bb8bba2cf3950eb2ef5e7ac77916203))
47
82
 
48
- ## [1.2.1](https://github.com/CoCreate-app/CoCreate-plugins/compare/v1.2.0...v1.2.1) (2026-03-02)
83
+ ## [1.2.1](https://github.com/CoCreateJS/CoCreate-plugins/compare/v1.2.0...v1.2.1) (2026-03-02)
49
84
 
50
85
 
51
86
  ### Bug Fixes
52
87
 
53
- * Handle case-insensitive plugin global mapping and add support for function reference assignments in plugin properties. ([904a947](https://github.com/CoCreate-app/CoCreate-plugins/commit/904a947abc868937b6e650b62e080d00341272a1))
88
+ * Handle case-insensitive plugin global mapping and add support for function reference assignments in plugin properties. ([904a947](https://github.com/CoCreateJS/CoCreate-plugins/commit/904a947abc868937b6e650b62e080d00341272a1))
54
89
 
55
- # [1.2.0](https://github.com/CoCreate-app/CoCreate-plugins/compare/v1.1.1...v1.2.0) (2026-02-15)
90
+ # [1.2.0](https://github.com/CoCreateJS/CoCreate-plugins/compare/v1.1.1...v1.2.0) (2026-02-15)
56
91
 
57
92
 
58
93
  ### Bug Fixes
59
94
 
60
- * improve key validation in executeGenericPlugin function ([447f6d2](https://github.com/CoCreate-app/CoCreate-plugins/commit/447f6d209adea010110c3bc52a17f8b45cb302ea))
95
+ * improve key validation in executeGenericPlugin function ([447f6d2](https://github.com/CoCreateJS/CoCreate-plugins/commit/447f6d209adea010110c3bc52a17f8b45cb302ea))
61
96
 
62
97
 
63
98
  ### Features
64
99
 
65
- * enhance demo page with improved layout and Bootstrap integration ([a74615e](https://github.com/CoCreate-app/CoCreate-plugins/commit/a74615e27be7c2f40529c5242e6c6e9b839ad020))
66
- * refactor plugin management and enhance dynamic loading capabilities ([4d91958](https://github.com/CoCreate-app/CoCreate-plugins/commit/4d919582934418cfb7bfa9497b6d0dcaf3bf8d10))
100
+ * enhance demo page with improved layout and Bootstrap integration ([a74615e](https://github.com/CoCreateJS/CoCreate-plugins/commit/a74615e27be7c2f40529c5242e6c6e9b839ad020))
101
+ * refactor plugin management and enhance dynamic loading capabilities ([4d91958](https://github.com/CoCreateJS/CoCreate-plugins/commit/4d919582934418cfb7bfa9497b6d0dcaf3bf8d10))
67
102
 
68
- ## [1.1.1](https://github.com/CoCreate-app/CoCreate-plugins/compare/v1.1.0...v1.1.1) (2026-02-09)
103
+ ## [1.1.1](https://github.com/CoCreateJS/CoCreate-plugins/compare/v1.1.0...v1.1.1) (2026-02-09)
69
104
 
70
105
 
71
106
  ### Bug Fixes
72
107
 
73
- * change init function to default export and update parameter handling ([654b623](https://github.com/CoCreate-app/CoCreate-plugins/commit/654b623760446cbd9db55cc216e25a07fe7865e3))
108
+ * change init function to default export and update parameter handling ([654b623](https://github.com/CoCreateJS/CoCreate-plugins/commit/654b623760446cbd9db55cc216e25a07fe7865e3))
74
109
 
75
- # [1.1.0](https://github.com/CoCreate-app/CoCreate-plugins/compare/v1.0.3...v1.1.0) (2026-01-15)
110
+ # [1.1.0](https://github.com/CoCreateJS/CoCreate-plugins/compare/v1.0.3...v1.1.0) (2026-01-15)
76
111
 
77
112
 
78
113
  ### Features
79
114
 
80
- * add marker to indicate where css files will be inserted ([6b4b030](https://github.com/CoCreate-app/CoCreate-plugins/commit/6b4b0301acdabff19b8cca5968cc67619e417c92))
115
+ * add marker to indicate where css files will be inserted ([6b4b030](https://github.com/CoCreateJS/CoCreate-plugins/commit/6b4b0301acdabff19b8cca5968cc67619e417c92))
81
116
 
82
- ## [1.0.3](https://github.com/CoCreate-app/CoCreate-plugins/compare/v1.0.2...v1.0.3) (2025-12-26)
117
+ ## [1.0.3](https://github.com/CoCreateJS/CoCreate-plugins/compare/v1.0.2...v1.0.3) (2025-12-26)
83
118
 
84
119
 
85
120
  ### Bug Fixes
86
121
 
87
- * update worklow ([8976d08](https://github.com/CoCreate-app/CoCreate-plugins/commit/8976d080f8972e840ff5530d5dfb15f839e9ac5b))
122
+ * update worklow ([8976d08](https://github.com/CoCreateJS/CoCreate-plugins/commit/8976d080f8972e840ff5530d5dfb15f839e9ac5b))
88
123
 
89
- ## [1.0.2](https://github.com/CoCreate-app/CoCreate-plugins/compare/v1.0.1...v1.0.2) (2025-12-25)
124
+ ## [1.0.2](https://github.com/CoCreateJS/CoCreate-plugins/compare/v1.0.1...v1.0.2) (2025-12-25)
90
125
 
91
126
 
92
127
  ### Bug Fixes
93
128
 
94
- * workflow ([3da40b3](https://github.com/CoCreate-app/CoCreate-plugins/commit/3da40b3572bdc84f1c555b2be4ae5e1fcc1a4533))
129
+ * workflow ([3da40b3](https://github.com/CoCreateJS/CoCreate-plugins/commit/3da40b3572bdc84f1c555b2be4ae5e1fcc1a4533))
95
130
 
96
- ## [1.0.1](https://github.com/CoCreate-app/CoCreate-plugins/compare/v1.0.0...v1.0.1) (2025-12-25)
131
+ ## [1.0.1](https://github.com/CoCreateJS/CoCreate-plugins/compare/v1.0.0...v1.0.1) (2025-12-25)
97
132
 
98
133
 
99
134
  ### Bug Fixes
100
135
 
101
- * workflow ([b1478b3](https://github.com/CoCreate-app/CoCreate-plugins/commit/b1478b3b6e014add5a7cb25fe83ce420147450f0))
102
- * workflow ([dbab32c](https://github.com/CoCreate-app/CoCreate-plugins/commit/dbab32cc403943faad190acf2f1b3284f14965b7))
136
+ * workflow ([b1478b3](https://github.com/CoCreateJS/CoCreate-plugins/commit/b1478b3b6e014add5a7cb25fe83ce420147450f0))
137
+ * workflow ([dbab32c](https://github.com/CoCreateJS/CoCreate-plugins/commit/dbab32cc403943faad190acf2f1b3284f14965b7))
103
138
 
104
139
  # 1.0.0 (2025-12-25)
105
140
 
106
141
 
107
142
  ### Bug Fixes
108
143
 
109
- * workflow ([044ec07](https://github.com/CoCreate-app/CoCreate-plugins/commit/044ec07b501298a17a61b604f37b9d5e76f1756c))
110
- * workflow ([cb2ec43](https://github.com/CoCreate-app/CoCreate-plugins/commit/cb2ec438b2b1e69f19f0fe29bf11791631d7fb67))
144
+ * workflow ([044ec07](https://github.com/CoCreateJS/CoCreate-plugins/commit/044ec07b501298a17a61b604f37b9d5e76f1756c))
145
+ * workflow ([cb2ec43](https://github.com/CoCreateJS/CoCreate-plugins/commit/cb2ec438b2b1e69f19f0fe29bf11791631d7fb67))
111
146
 
112
147
 
113
148
  ### Features
114
149
 
115
- * Initial Release ([4d46a70](https://github.com/CoCreate-app/CoCreate-plugins/commit/4d46a70880c086733fa175531096d20f04a7f149))
150
+ * Initial Release ([4d46a70](https://github.com/CoCreateJS/CoCreate-plugins/commit/4d46a70880c086733fa175531096d20f04a7f149))
package/CONTRIBUTING.md CHANGED
@@ -1,19 +1,19 @@
1
1
  # Contributing to CoCreate-plugins
2
2
 
3
- This project is work of [many contributors](https://github.com/CoCreate-app/CoCreate-plugins/graphs/contributors).
4
- You're encouraged to submit [pull requests](https://github.com/CoCreate-app/CoCreate-plugins/pulls),
5
- [propose features and discuss issues](https://github.com/CoCreate-app/CoCreate-plugins/issues).
3
+ This project is work of [many contributors](https://github.com/CoCreateJS/plugins/graphs/contributors).
4
+ You're encouraged to submit [pull requests](https://github.com/CoCreateJS/plugins/pulls),
5
+ [propose features and discuss issues](https://github.com/CoCreateJS/plugins/issues).
6
6
 
7
7
  In the examples below, substitute your Github username for `contributor` in URLs.
8
8
 
9
9
  ## Fork the Project
10
10
 
11
- Fork the [project on Github](https://github.com/CoCreate-app/CoCreate-plugins) and check out your copy.
11
+ Fork the [project on Github](https://github.com/CoCreateJS/plugins) and check out your copy.
12
12
 
13
13
  ```
14
14
  git clone https://github.com/contributor/CoCreate-plugins.git
15
15
  cd CoCreate-plugins
16
- git remote add upstream https://gitlab.com/cocreatejs/CoCreate-plugins.git
16
+ git remote add upstream https://github.com/CoCreateJS/plugins.git
17
17
  ```
18
18
 
19
19
  ## Create a Topic Branch
@@ -74,7 +74,7 @@ git push origin my-feature-branch
74
74
 
75
75
  ## Make a Pull Request
76
76
 
77
- Go to [https://github.com/CoCreate-app/CoCreate-plugins](https://github.com/CoCreate-app/CoCreate-plugins) and select your feature branch.
77
+ Go to [https://github.com/CoCreateJS/plugins](https://github.com/CoCreateJS/plugins) and select your feature branch.
78
78
  Click the 'Pull Request' button and fill out the form. Pull requests are usually reviewed within a few days.
79
79
 
80
80
  ## Rebase
package/README.md CHANGED
@@ -6,7 +6,7 @@ A declarative browser plugin loader and runtime for CoCreate. It loads registere
6
6
 
7
7
  ## Documentation
8
8
 
9
- For interactive examples, visit the [CoCreate Plugins documentation](https://cocreate.app/docs/plugins).
9
+ For interactive examples, visit the [CoCreate Plugins documentation](https://CoCreateJS.com/docs/plugins).
10
10
 
11
11
  ## Table of Contents
12
12
 
@@ -27,13 +27,32 @@ For interactive examples, visit the [CoCreate Plugins documentation](https://coc
27
27
 
28
28
  ---
29
29
 
30
+ ## Explicit instance storage
31
+
32
+ Plugin results are not automatically assigned to the host element. To retain a
33
+ returned instance, explicitly assign it using AST:
34
+
35
+ ```html
36
+ <div plugin='{
37
+ "$this.myInstance": {
38
+ "$ExamplePlugin": ["$this", {}]
39
+ }
40
+ }'></div>
41
+ ```
42
+
43
+ Here `$this` identifies the host element and `myInstance` names the property to
44
+ write. The nested initializer executes first, then its returned value is
45
+ assigned to that property. A global plugin call that needs no retained result
46
+ can run without this assignment. Code that previously relied on automatic `element.PluginName`
47
+ storage must explicitly assign and reference the instance instead.
48
+
30
49
  ## Architectural Overview
31
50
 
32
51
  `@cocreate/plugins` connects three parts of the browser runtime:
33
52
 
34
53
  1. A plugin definition in `window.CoCreateConfig.plugins` identifies the JavaScript and CSS resources.
35
54
  2. A DOM element's `plugin` attribute contains an AST configuration.
36
- 3. The resolved plugin instance and other top-level AST values are attached to that element.
55
+ 3. AST executes the configuration. Results are stored only where the configuration explicitly assigns them.
37
56
 
38
57
  ```text
39
58
  CoCreateConfig.plugins
@@ -46,10 +65,10 @@ window.PluginName
46
65
  ├── AST executes plugin configuration
47
66
  │
48
67
  ▼
49
- element.PluginName
68
+ result (optionally assigned through AST)
50
69
  ```
51
70
 
52
- Plugin configurations execute sequentially. A constructor result created by one top-level key is therefore available to every key that follows it.
71
+ Plugin operations execute sequentially. Plugin names continue to refer to their global libraries; constructor results do not automatically replace those references or attach to the element.
53
72
 
54
73
  The module observes `[plugin]` elements automatically, including elements added or updated after the initial page load.
55
74
 
@@ -72,7 +91,7 @@ yarn add @cocreate/plugins
72
91
  ### CDN
73
92
 
74
93
  ```html
75
- <script src="https://cdn.cocreate.app/plugins/latest/CoCreate-plugins.min.js"></script>
94
+ <script src="https://cdn.CoCreateJS.com/plugins/latest/CoCreate-plugins.min.js"></script>
76
95
  ```
77
96
 
78
97
  When using the package as a module:
@@ -141,12 +160,12 @@ Registration describes where a library is loaded from. Initialization behavior b
141
160
 
142
161
  ### Constructor or Function
143
162
 
144
- The simplest object form calls the registered global and assigns its result to the element under the same name.
163
+ Use an explicit AST call to initialize a plugin and a dotted destination to retain its result on the element.
145
164
 
146
165
  ```html
147
166
  <div
148
167
  plugin='{
149
- "SimpleBar": ["$this", {}]
168
+ "$this.SimpleBar": { "$SimpleBar": ["$this", {}] }
150
169
  }'
151
170
  style="max-height: 300px">
152
171
  </div>
@@ -165,11 +184,13 @@ For a global function that receives a single options object:
165
184
  ```html
166
185
  <button
167
186
  plugin='{
168
- "Toastify": {
169
- "text": "Saved successfully",
170
- "duration": 3000
187
+ "$this.toast": {
188
+ "$Toastify": {
189
+ "text": "Saved successfully",
190
+ "duration": 3000
191
+ }
171
192
  },
172
- "Toastify.showToast": []
193
+ "$this.toast.showToast": []
173
194
  }'>
174
195
  Show notification
175
196
  </button>
@@ -183,21 +204,18 @@ Some libraries expose constructors as properties of a global namespace. A single
183
204
  <div
184
205
  id="calendar"
185
206
  plugin='{
186
- "FullCalendar": {
187
- "Calendar": [
207
+ "$this.FullCalendar": {
208
+ "$FullCalendar.Calendar": [
188
209
  "$this",
189
- {
190
- "initialView": "dayGridMonth",
191
- "editable": true
192
- }
210
+ { "initialView": "dayGridMonth", "editable": true }
193
211
  ]
194
212
  },
195
- "FullCalendar.render": []
213
+ "$this.FullCalendar.render": []
196
214
  }'>
197
215
  </div>
198
216
  ```
199
217
 
200
- The first key creates the instance:
218
+ The first key explicitly creates and stores the instance:
201
219
 
202
220
  ```javascript
203
221
  element.FullCalendar = new window.FullCalendar.Calendar(element, options);
@@ -226,8 +244,8 @@ The attribute can contain an array. Each object is processed in order:
226
244
  ```html
227
245
  <div
228
246
  plugin='[
229
- { "FirstPlugin": ["$this"] },
230
- { "SecondPlugin": ["$this", {}] }
247
+ { "$FirstPlugin": ["$this"] },
248
+ { "$SecondPlugin": ["$this", {}] }
231
249
  ]'>
232
250
  </div>
233
251
  ```
@@ -236,29 +254,23 @@ The attribute can contain an array. Each object is processed in order:
236
254
 
237
255
  ## AST Execution Model
238
256
 
239
- Each configuration object is converted into one AST program and evaluated sequentially.
257
+ Each JSON configuration object is passed unchanged to `astAsync`, once, with
258
+ the registered libraries and `$this` in its sandbox and the host element as
259
+ `node`. The plugin loader does not wrap individual entries or rewrite ordinary
260
+ keys into calls. Sibling entries share AST's evaluation scope, including values
261
+ created by earlier entries. Configuration arrays execute one block at a time.
262
+ The bare-name shorthand is expanded to `{ "$PluginName": [] }`.
240
263
 
241
264
  ```json
242
265
  {
243
- "FullCalendar": {
244
- "Calendar": ["$this", {}]
245
- },
246
- "FullCalendar.render": []
266
+ "instance": { "$FullCalendar.Calendar": ["$this", {}] },
267
+ "$this.calendar": "$instance",
268
+ "$instance.render": []
247
269
  }
248
270
  ```
249
271
 
250
- Conceptually becomes:
251
-
252
- ```json
253
- {
254
- "FullCalendar": {
255
- "$FullCalendar.Calendar": ["$this", {}]
256
- },
257
- "FullCalendar.render": {
258
- "$FullCalendar.render": []
259
- }
260
- }
261
- ```
272
+ JSON configs that previously relied on implicit plugin calls must use explicit
273
+ `$PluginName` or `$PluginName.method` keys. Ordinary keys declare AST data.
262
274
 
263
275
  Execution follows these rules:
264
276
 
@@ -269,22 +281,22 @@ Execution follows these rules:
269
281
  - Native classes are instantiated with `new`.
270
282
  - Functions are called normally.
271
283
  - A resolved top-level value becomes available to subsequent keys.
272
- - Resolved top-level keys without dot notation are attached to the element.
273
- - Dotted keys represent operations and are not attached as element properties.
284
+ - No result is automatically attached to the element, including `$return` results.
285
+ - Use an explicit AST assignment to retain an instance on `$this` or another destination.
274
286
 
275
287
  Ordinary non-plugin keys remain part of the AST program, so they can store intermediate results or participate in later operations.
276
288
 
277
289
  ```html
278
290
  <div
279
291
  plugin='{
280
- "ExamplePlugin": ["$this"],
281
- "items": { "$ExamplePlugin.getItems": [] },
292
+ "instance": { "$ExamplePlugin": ["$this"] },
293
+ "items": { "$instance.getItems": [] },
282
294
  "count": "$items.length"
283
295
  }'>
284
296
  </div>
285
297
  ```
286
298
 
287
- After evaluation, `element.ExamplePlugin`, `element.items`, and `element.count` contain the corresponding resolved values.
299
+ Top-level result keys do not automatically become element properties. Use an explicit assignment to store values on the element.
288
300
 
289
301
  ---
290
302
 
@@ -414,19 +426,19 @@ AST configuration controls how an already trusted plugin is initialized; it does
414
426
 
415
427
  ## Announcements
416
428
 
417
- Updates are documented in the project [CHANGELOG](https://github.com/CoCreate-app/CoCreate-plugins/blob/master/CHANGELOG.md) and [releases](https://github.com/CoCreate-app/CoCreate-plugins/releases).
429
+ Updates are documented in the project [CHANGELOG](https://github.com/CoCreateJS/plugins/blob/master/CHANGELOG.md) and [releases](https://github.com/CoCreateJS/plugins/releases).
418
430
 
419
431
  ## Roadmap
420
432
 
421
- See the open [issues](https://github.com/CoCreate-app/CoCreate-plugins/issues) and [pull requests](https://github.com/CoCreate-app/CoCreate-plugins/pulls) for current development priorities.
433
+ See the open [issues](https://github.com/CoCreateJS/plugins/issues) and [pull requests](https://github.com/CoCreateJS/plugins/pulls) for current development priorities.
422
434
 
423
435
  ## How to Contribute
424
436
 
425
- Contributions are welcome. See [CONTRIBUTING.md](https://github.com/CoCreate-app/CoCreate-plugins/blob/master/CONTRIBUTING.md) before opening an issue or pull request.
437
+ Contributions are welcome. See [CONTRIBUTING.md](https://github.com/CoCreateJS/plugins/blob/master/CONTRIBUTING.md) before opening an issue or pull request.
426
438
 
427
439
  ## License
428
440
 
429
441
  This software is dual-licensed under the GNU Affero General Public License version 3 (AGPLv3) and a commercial license.
430
442
 
431
- - **Open-source use:** Modifications and derivative works must comply with the AGPLv3. See the [LICENSE](https://github.com/CoCreate-app/CoCreate-plugins/blob/master/LICENSE).
432
- - **Commercial use:** A commercial license is available from [CoCreate](https://cocreate.app).
443
+ - **Open-source use:** Modifications and derivative works must comply with the AGPLv3. See the [LICENSE](https://github.com/CoCreateJS/plugins/blob/master/LICENSE).
444
+ - **Commercial use:** A commercial license is available from [CoCreate](https://CoCreateJS.com).
package/docs/index.html CHANGED
@@ -9,7 +9,7 @@
9
9
  rel="icon"
10
10
  type="image/png"
11
11
  sizes="32x32"
12
- href="https://cocreate.app/images/favicon.ico" />
12
+ href="https://CoCreateJS.com/images/favicon.ico" />
13
13
  <meta
14
14
  name="description"
15
15
  content="A simple HTML5 and pure javascript component. Easy configuration using HTML5 attributes or Javscript api and highly styleable." />
@@ -59,7 +59,7 @@
59
59
  share-width="500"
60
60
  share-media="https://via.placeholder.com/300/09f/fff.png">
61
61
  <a
62
- href="https://github.com/CoCreate-app/CoCreate-plugins"
62
+ href="https://github.com/CoCreateJS/CoCreate-plugins"
63
63
  target="_blank"
64
64
  class="margin-right:15px"
65
65
  ><i src="/assets/svg/github.svg"></i
@@ -103,7 +103,7 @@
103
103
  <pre
104
104
  class="margin-top:15px"><code class="language-javascript">npm install cocreate-plugins</code></pre>
105
105
  <p class="padding:10px_0px">Or you can use cdn link:</p>
106
- <pre><code class="language-javascript">https://cdn.cocreate.app/js/CoCreate-plugins.min.js</code></pre>
106
+ <pre><code class="language-javascript">https://cdn.CoCreateJS.com/js/CoCreate-plugins.min.js</code></pre>
107
107
 
108
108
  <h2
109
109
  class="border-bottom:1px_solid_lightgrey margin-top:20px padding:5px_0px">
@@ -247,6 +247,6 @@
247
247
  </div>
248
248
  </main>
249
249
 
250
- <script src="https://CoCreate.app/dist/CoCreate.js"></script>
250
+ <script src="https://CoCreateJS.com/dist/CoCreate.js"></script>
251
251
  </body>
252
252
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocreate/plugins",
3
- "version": "1.9.0",
3
+ "version": "2.0.0",
4
4
  "description": "CoCreate plugins",
5
5
  "author": "CoCreate LLC",
6
6
  "license": "AGPL-3.0",
@@ -14,15 +14,15 @@
14
14
  },
15
15
  "repository": {
16
16
  "type": "git",
17
- "url": "git+https://gitlab.com/cocreatejs/CoCreate-plugins.git"
17
+ "url": "git+https://github.com/CoCreateJS/plugins.git"
18
18
  },
19
19
  "bugs": {
20
- "url": "https://github.com/CoCreate-app/CoCreate-plugins/issues"
20
+ "url": "https://github.com/CoCreateJS/plugins/issues"
21
21
  },
22
- "homepage": "https://cocreate.app/docs/plugins",
22
+ "homepage": "https://CoCreateJS.com/docs/plugins",
23
23
  "funding": {
24
24
  "type": "GitHub Sponsors ❤",
25
- "url": "https://github.com/sponsors/CoCreate-app"
25
+ "url": "https://github.com/sponsors/CoCreateJS"
26
26
  },
27
27
  "type": "module",
28
28
  "main": "./src/index.js",
package/src/index.js CHANGED
@@ -4,6 +4,7 @@ import { astAsync } from '@cocreate/utils';
4
4
  const scriptCache = new Map();
5
5
  const pendingElements = new WeakSet();
6
6
  const initializedPlugins = new WeakMap();
7
+ const initializingPlugins = new WeakMap();
7
8
  const cssMarker = typeof document !== 'undefined'
8
9
  ? document.head.querySelector('meta[name="viewport"]') || document.head.lastChild
9
10
  : null;
@@ -11,6 +12,8 @@ const cssMarker = typeof document !== 'undefined'
11
12
  /**
12
13
  * Retrieves the global plugin configuration object from window.CoCreateConfig.
13
14
  * @returns {Object}
15
+ * @private
16
+ * @environment client
14
17
  */
15
18
  function getPluginsConfig() {
16
19
  if (typeof window !== 'undefined' && window.CoCreateConfig && window.CoCreateConfig.plugins) {
@@ -23,6 +26,8 @@ function getPluginsConfig() {
23
26
  * Dynamically registers or updates a plugin definition in global config.
24
27
  * @param {string} name - Plugin identifier
25
28
  * @param {Object} config - Plugin configuration object (js, css, etc.)
29
+ * @public
30
+ * @environment client
26
31
  */
27
32
  export function registerPlugin(name, config) {
28
33
  if (typeof window !== 'undefined') {
@@ -37,6 +42,8 @@ export function registerPlugin(name, config) {
37
42
  * Sets crossOrigin="anonymous" by default for simple string URLs.
38
43
  * @param {string|Object} item - Script URL or object with configuration
39
44
  * @returns {Promise<void>}
45
+ * @private
46
+ * @environment client
40
47
  */
41
48
  function loadScript(item) {
42
49
  const src = typeof item === 'string' ? item : item?.src;
@@ -76,6 +83,8 @@ function loadScript(item) {
76
83
  * Sets crossOrigin="anonymous" by default for simple string URLs.
77
84
  * @param {string|Object} item - Stylesheet URL or object with configuration
78
85
  * @returns {Promise<void>}
86
+ * @private
87
+ * @environment client
79
88
  */
80
89
  function loadCSS(item) {
81
90
  const href = typeof item === 'string' ? item : item?.href;
@@ -115,6 +124,8 @@ function loadCSS(item) {
115
124
  * Loads each plugin's JavaScript resources in declared order while allowing
116
125
  * independent plugins and stylesheets to load in parallel.
117
126
  * @param {Array<string>} pluginNames - Names of target plugins to preload
127
+ * @private
128
+ * @environment client
118
129
  */
119
130
  async function preloadResources(pluginNames) {
120
131
  const plugins = getPluginsConfig();
@@ -152,6 +163,8 @@ async function preloadResources(pluginNames) {
152
163
  * @param {number} retries - Number of retries
153
164
  * @param {number} delay - Retry interval in ms
154
165
  * @returns {Promise<Function|Object|null>}
166
+ * @private
167
+ * @environment client
155
168
  */
156
169
  async function waitForPluginOnWindow(pluginName, retries = 5, delay = 50) {
157
170
  for (let i = 0; i < retries; i++) {
@@ -166,6 +179,8 @@ async function waitForPluginOnWindow(pluginName, retries = 5, delay = 50) {
166
179
  * Helper to resolve and normalize plugin library constructor from window object.
167
180
  * @param {string} pluginName - The target plugin identifier
168
181
  * @returns {Function|Object|null}
182
+ * @private
183
+ * @environment client
169
184
  */
170
185
  function getPluginLibrary(pluginName) {
171
186
  if (typeof window === 'undefined') return null;
@@ -199,87 +214,32 @@ function getPluginNames(configBlock) {
199
214
  return pluginNames;
200
215
  }
201
216
 
202
- function isClassLike(value) {
203
- if (typeof value !== 'function') return false;
204
-
205
- const source = Function.prototype.toString.call(value);
206
- if (/^class\s|^class{/.test(source)) return true;
207
-
208
- return !!value.prototype && Object.getOwnPropertyNames(value.prototype)
209
- .some(member => member !== 'constructor');
210
- }
211
-
212
- function getPluginMember(library, path) {
213
- let value = library;
214
-
215
- for (const property of path) {
216
- if (value === null || value === undefined || !(property in Object(value))) {
217
- return undefined;
218
- }
219
- value = value[property];
220
- }
221
-
222
- return value;
223
- }
224
-
225
- function createPluginOperations(configBlock, pluginNames, libraries) {
226
- const operations = [];
227
-
228
- for (const [key, value] of Object.entries(configBlock)) {
229
- const normalizedKey = key.replace(/^\$/, '');
230
- const pluginName = normalizedKey.split('.')[0];
231
-
232
- if (!pluginNames.has(pluginName)) {
233
- operations.push({ ast: { [key]: value } });
234
- continue;
235
- }
236
-
237
- let operator = normalizedKey;
238
- let parameters = value;
239
- const library = libraries['$' + pluginName];
240
- const memberPath = normalizedKey.split('.').slice(1);
241
- const pluginMember = getPluginMember(library, memberPath);
242
- const members = value && typeof value === 'object' && !Array.isArray(value)
243
- ? Object.entries(value)
244
- : [];
245
-
246
- if (
247
- !normalizedKey.includes('.') &&
248
- typeof library !== 'function' &&
249
- members.length === 1
250
- ) {
251
- const [member, memberParameters] = members[0];
252
- operator = `${pluginName}.${member.replace(/^\$/, '')}`;
253
- parameters = memberParameters;
254
- }
255
-
256
- operations.push({
257
- ast: { ['$' + operator]: parameters },
258
- pluginName,
259
- attach: !normalizedKey.includes('.') || isClassLike(pluginMember)
260
- });
261
- }
262
-
263
- return operations;
264
- }
265
-
266
217
  /**
267
218
  * Parses element 'plugin' attribute, executes AST configuration via astAsync,
268
- * sets core library on window, and attaches instance onto element property.
219
+ * with result storage controlled explicitly by the AST configuration.
269
220
  * @param {Element} el - DOM element with plugin attribute
221
+ * @private
222
+ * @environment client
270
223
  */
271
- async function processPlugin(el) {
272
- if (!el || !el.attributes) return;
224
+ function processPlugin(el) {
225
+ if (!el || !el.attributes) return Promise.resolve();
273
226
 
274
227
  const pluginAttrNode = el.attributes['plugin'] || el.attributes.getNamedItem?.('plugin');
275
228
  const rawPluginAttr = pluginAttrNode?.value?.trim();
276
- if (!rawPluginAttr) return;
229
+ if (!rawPluginAttr) return Promise.resolve();
277
230
 
278
- // Added-node and attribute observers can discover the same element during
279
- // separate mutation cycles. Do not initialize an unchanged plugin twice.
280
- if (initializedPlugins.get(el) === rawPluginAttr) return;
281
- initializedPlugins.set(el, rawPluginAttr);
231
+ const pending = initializingPlugins.get(el);
232
+ if (pending?.value === rawPluginAttr) return pending.promise;
233
+ if (initializedPlugins.get(el) === rawPluginAttr) return Promise.resolve();
234
+
235
+ const promise = initializePlugin(el, rawPluginAttr).finally(() => {
236
+ if (initializingPlugins.get(el)?.promise === promise) initializingPlugins.delete(el);
237
+ });
238
+ initializingPlugins.set(el, { value: rawPluginAttr, promise });
239
+ return promise;
240
+ }
282
241
 
242
+ async function initializePlugin(el, rawPluginAttr) {
283
243
  try {
284
244
  let configs = [];
285
245
 
@@ -287,7 +247,7 @@ async function processPlugin(el) {
287
247
  const parsedConfig = JSON.parse(rawPluginAttr);
288
248
  configs = Array.isArray(parsedConfig) ? parsedConfig : [parsedConfig];
289
249
  } else {
290
- configs = [{ [rawPluginAttr]: [] }];
250
+ configs = [{ ['$' + rawPluginAttr.replace(/^\$/, '')]: [] }];
291
251
  }
292
252
 
293
253
  const configPlans = configs.map(configBlock => ({
@@ -313,39 +273,13 @@ async function processPlugin(el) {
313
273
 
314
274
  const scope = { ...libraries, $this: el };
315
275
 
316
- if (Object.prototype.hasOwnProperty.call(configBlock, '$return')) {
317
- const value = await astAsync({
318
- executeAST: configBlock,
319
- sandbox: scope,
320
- node: el
321
- });
322
-
323
- if (value !== undefined && pluginNames.size === 1) {
324
- const [pluginName] = pluginNames;
325
- el[pluginName] = value;
326
- }
327
- continue;
328
- }
329
-
330
- const operations = createPluginOperations(
331
- configBlock,
332
- pluginNames,
333
- libraries
334
- );
335
-
336
- for (const operation of operations) {
337
- const value = await astAsync({
338
- executeAST: operation.ast,
339
- sandbox: scope,
340
- node: el
341
- });
342
-
343
- if (operation.attach && value !== undefined) {
344
- scope['$' + operation.pluginName] = value;
345
- el[operation.pluginName] = value;
346
- }
347
- }
276
+ await astAsync({
277
+ executeAST: configBlock,
278
+ sandbox: scope,
279
+ node: el
280
+ });
348
281
  }
282
+ initializedPlugins.set(el, rawPluginAttr);
349
283
  } catch (err) {
350
284
  initializedPlugins.delete(el);
351
285
  console.error('[Plugin System] Failed to parse and execute plugin configuration:', err, rawPluginAttr);
@@ -355,6 +289,8 @@ async function processPlugin(el) {
355
289
  /**
356
290
  * Initializes plugins on single elements, collections, or selector query matches.
357
291
  * @param {Element|NodeList|Array|string} elements - Target element(s) or DOM query selector
292
+ * @public
293
+ * @environment client
358
294
  */
359
295
  export function init(elements) {
360
296
  if (!elements) return Promise.resolve([]);
@@ -402,3 +338,33 @@ if (typeof document !== 'undefined') {
402
338
  }
403
339
 
404
340
  export default { init, registerPlugin };
341
+
342
+ /**
343
+ *
344
+ * @name plugin
345
+ * @kind member
346
+ * @attribute
347
+ * @environment client
348
+ */
349
+
350
+ /**
351
+ * CoCreate plugins
352
+ *
353
+ * @module plugins
354
+ * @environment client
355
+ */
356
+
357
+ /**
358
+ * Declarative selectors recognized by this module.
359
+ *
360
+ * @name declarative-selectors
361
+ * @kind member
362
+ * @selectors
363
+ * @environment client
364
+ * @example
365
+ * ```json
366
+ * [
367
+ * "[plugin]"
368
+ * ]
369
+ * ```
370
+ */
@@ -0,0 +1,212 @@
1
+ import assert from 'node:assert/strict';
2
+ import fs from 'node:fs';
3
+ import vm from 'node:vm';
4
+ import test from 'node:test';
5
+ import { astAsync } from '@cocreate/utils';
6
+
7
+ const code = fs.readFileSync(new URL('../src/index.js', import.meta.url), 'utf8')
8
+ .replace(/^import .*;\r?\n/gm, '')
9
+ .replace(/^export /gm, '')
10
+ .replace('default { init, registerPlugin };', 'globalThis.pluginInit = init;');
11
+
12
+ class Element {
13
+ constructor(config) {
14
+ this.attributes = { plugin: { value: typeof config === 'string' ? config : JSON.stringify(config) } };
15
+ }
16
+ }
17
+
18
+ function runtime(libraries, evaluate = astAsync) {
19
+ const errors = [];
20
+ const context = {
21
+ astAsync: evaluate, Element,
22
+ window: { ...libraries, Object },
23
+ console: { error: (...args) => errors.push(args), warn: (...args) => errors.push(args) }
24
+ };
25
+ vm.runInNewContext(code, context);
26
+ return {
27
+ async init(element) {
28
+ await context.pluginInit(element);
29
+ assert.deepEqual(errors, []);
30
+ }
31
+ };
32
+ }
33
+
34
+ test('bare plugin initialization does not attach its returned instance', async () => {
35
+ let calls = 0;
36
+ class ExamplePlugin {
37
+ constructor() { calls++; }
38
+ }
39
+ const app = runtime({ ExamplePlugin });
40
+ const el = new Element('ExamplePlugin');
41
+ await app.init(el);
42
+ await app.init(el);
43
+ assert.equal(calls, 1);
44
+ assert.equal(Object.hasOwn(el, 'ExamplePlugin'), false);
45
+ });
46
+
47
+ test('nested constructors and $return results do not attach automatically', async () => {
48
+ class Widget {}
49
+ const app = runtime({ Library: { Widget } });
50
+ for (const config of [
51
+ { '$Library.Widget': [] },
52
+ { $return: { '$Library.Widget': [] } }
53
+ ]) {
54
+ const el = new Element(config);
55
+ await app.init(el);
56
+ assert.equal(Object.hasOwn(el, 'Library'), false);
57
+ }
58
+ });
59
+
60
+ test('AST can explicitly store a returned instance on $this under a chosen key', async () => {
61
+ class ExamplePlugin {}
62
+ const app = runtime({ ExamplePlugin });
63
+ const el = new Element({
64
+ '$this.customInstance': { $ExamplePlugin: [] }
65
+ });
66
+ await app.init(el);
67
+ assert.ok(el.customInstance instanceof ExamplePlugin);
68
+ assert.equal(Object.hasOwn(el, 'ExamplePlugin'), false);
69
+ });
70
+
71
+ test('AST can store results elsewhere and call methods on explicit references', async () => {
72
+ class ExamplePlugin {
73
+ render() { this.rendered = true; }
74
+ }
75
+ const destination = {};
76
+ const app = runtime({ ExamplePlugin, destination });
77
+ const el = new Element({
78
+ instance: { $ExamplePlugin: [] },
79
+ '$instance.render': [],
80
+ $return: { '$destination': { '$Object.assign': ['$destination', { instance: '$instance' }] } }
81
+ });
82
+ await app.init(el);
83
+ assert.ok(destination.instance instanceof ExamplePlugin);
84
+ assert.equal(destination.instance.rendered, true);
85
+ assert.equal(Object.hasOwn(el, 'ExamplePlugin'), false);
86
+ assert.equal(Object.hasOwn(el, 'instance'), false);
87
+ });
88
+
89
+ test('subsequent operations keep referencing a global library after a call returns a value', async () => {
90
+ let calls = 0;
91
+ function GlobalPlugin() { return {}; }
92
+ GlobalPlugin.highlightAll = () => { calls++; };
93
+ const app = runtime({ GlobalPlugin });
94
+ const el = new Element({ '$GlobalPlugin': [], '$GlobalPlugin.highlightAll': [] });
95
+ await app.init(el);
96
+ assert.equal(calls, 1);
97
+ assert.equal(Object.hasOwn(el, 'GlobalPlugin'), false);
98
+ });
99
+
100
+ test('passes the entire unchanged configuration to AST once with the host and library scope', async () => {
101
+ const config = {
102
+ created: { $ExamplePlugin: ['$this'] },
103
+ '$this.instance': '$created',
104
+ '$this.instance.render': []
105
+ };
106
+ class ExamplePlugin {
107
+ constructor(host) { this.host = host; }
108
+ render() { this.rendered = true; }
109
+ }
110
+ const calls = [];
111
+ const app = runtime({ ExamplePlugin }, async options => {
112
+ calls.push(options);
113
+ return astAsync(options);
114
+ });
115
+ const el = new Element(config);
116
+ await app.init(el);
117
+ assert.equal(calls.length, 1);
118
+ assert.deepEqual(JSON.parse(JSON.stringify(calls[0].executeAST)), config);
119
+ assert.equal(calls[0].sandbox.$this, el);
120
+ assert.equal(calls[0].sandbox.$ExamplePlugin, ExamplePlugin);
121
+ assert.equal(calls[0].node, el);
122
+ assert.ok(el.instance instanceof ExamplePlugin);
123
+ assert.equal(el.instance.host, el);
124
+ assert.equal(el.instance.rendered, true);
125
+ });
126
+
127
+ test('plain keys store data rather than being rewritten into plugin calls', async () => {
128
+ let calls = 0;
129
+ function ExamplePlugin() { calls++; }
130
+ const app = runtime({ ExamplePlugin });
131
+ const el = new Element({
132
+ ExamplePlugin: ['configuration data'],
133
+ '$this.saved': '$ExamplePlugin'
134
+ });
135
+ await app.init(el);
136
+ assert.equal(calls, 0);
137
+ assert.equal(el.saved, ExamplePlugin);
138
+ });
139
+
140
+ test('configuration blocks execute in order without special treatment for $return', async () => {
141
+ const order = [];
142
+ function ExamplePlugin(value) { order.push(value); return value; }
143
+ const app = runtime({ ExamplePlugin });
144
+ const el = new Element([
145
+ { '$this.first': { $ExamplePlugin: ['first'] } },
146
+ { $return: { '$this.second': { $ExamplePlugin: ['$this.first'] } } }
147
+ ]);
148
+ await app.init(el);
149
+ assert.deepEqual(order, ['first', 'first']);
150
+ assert.equal(el.first, 'first');
151
+ assert.equal(el.second, 'first');
152
+ assert.equal(Object.hasOwn(el, 'ExamplePlugin'), false);
153
+ });
154
+
155
+ test('concurrent initialization calls await the same pending setup', async () => {
156
+ let release;
157
+ let entered;
158
+ let calls = 0;
159
+ const gate = new Promise(resolve => { release = resolve; });
160
+ const started = new Promise(resolve => { entered = resolve; });
161
+ const app = runtime({ ExamplePlugin() {} }, async args => {
162
+ calls++;
163
+ entered();
164
+ await gate;
165
+ return astAsync(args);
166
+ });
167
+ const element = new Element('ExamplePlugin');
168
+ const first = app.init(element);
169
+ await started;
170
+ let secondFinished = false;
171
+ const second = app.init(element).then(() => { secondFinished = true; });
172
+ await Promise.resolve();
173
+ await Promise.resolve();
174
+ assert.equal(secondFinished, false);
175
+ assert.equal(calls, 1);
176
+ release();
177
+ await Promise.all([first, second]);
178
+ assert.equal(secondFinished, true);
179
+ await app.init(element);
180
+ assert.equal(calls, 1);
181
+ });
182
+
183
+ test('a plugin can await another initializer before connecting its result', async () => {
184
+ let release;
185
+ let entered;
186
+ let connected = false;
187
+ const gate = new Promise(resolve => { release = resolve; });
188
+ const started = new Promise(resolve => { entered = resolve; });
189
+ const markdown = new Element({ '$this.parser': { $Parser: [] } });
190
+ const CoCreate = { plugins: { init: () => app.init(markdown) } };
191
+ const app = runtime({
192
+ CoCreate,
193
+ async Parser() {
194
+ entered();
195
+ await gate;
196
+ return { ready: true };
197
+ },
198
+ connect() {
199
+ assert.equal(markdown.parser.ready, true);
200
+ connected = true;
201
+ }
202
+ });
203
+ const prism = new Element({ '$CoCreate.plugins.init': ['#markdown'], $connect: [] });
204
+ const first = app.init(markdown);
205
+ await started;
206
+ const second = app.init(prism);
207
+ await Promise.resolve();
208
+ assert.equal(connected, false);
209
+ release();
210
+ await Promise.all([first, second]);
211
+ assert.equal(connected, true);
212
+ });
File without changes