@vcmap/plugin-cli 2.0.7 → 2.0.10

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.
package/LICENSE.md CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2021 virtualcitySYSTEMS GmbH
1
+ Copyright 2022 virtualcitySYSTEMS GmbH
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
4
 
package/README.md CHANGED
@@ -79,7 +79,32 @@ By default, this will launch a dev server at localhost:5005 using the @vcmap/ui
79
79
  as its base. Alternatively you can provide a URL to a hosted VC Map application
80
80
  and use said application as its base instead.
81
81
 
82
- ### 4. Building a plugin
82
+ ### 4. Building a plugin staging application
83
+
84
+ A staging application creates a full deployable VC Map in the `dist` folder with the following components.
85
+ - compiled @vcmap/ui library and all dependencies
86
+ - default @vcmap/ui configurations
87
+ - default @vcmap/ui plugins
88
+ - compiled plugin which is in development.
89
+
90
+ Building the staging application will collect all parts and will inject the plugin in development in the default
91
+ map configuration. The staging application can for example be used to deploy the App in an Apache in a postCommit
92
+ Pipeline. (See .gitlab-ci.yml for an example).
93
+ ```bash
94
+ npx vcmplugin buildStagingApp
95
+ ```
96
+ To start a webserver to serve the content of the `dist` folder call `npx vite preview`; This will start a static webserver
97
+ on the port 4173.
98
+
99
+ The Dockerfile in `build/staging/Dockerfile` can be used to create a Docker Container which serves the content of the dist folder.
100
+ ```bash
101
+ npx vcmplugin buildStagingApp
102
+ cd dist
103
+ docker build -f ../build/staging/Dockerfile -t vcmap:staging .
104
+ docker run --rm -p 5000:80 vcmap:staging
105
+ ```
106
+
107
+ ### 5. Building a plugin
83
108
 
84
109
  To build your project, run the following from within your projects root:
85
110
  ```bash
@@ -87,7 +112,7 @@ npx vcmplugin build
87
112
  ```
88
113
  This will build your application and place it in the `dist` directory.
89
114
 
90
- ### 5. Integrating a plugin in a productive VC MAP
115
+ ### 6. Integrating a plugin in a productive VC MAP
91
116
 
92
117
  To pack your project for productive use, run the following from within your projects root:
93
118
  ```bash
@@ -114,7 +139,6 @@ as peer dependencies if you use them in your plugin:
114
139
  - @vcmap/cesium
115
140
  - ol
116
141
  - vue
117
- - @vue/composition-api
118
142
  - vuetify
119
143
 
120
144
  During the build step, these libraries are automatically externalized by the vcmplugin-cli and in
@@ -154,6 +178,7 @@ to create your project, a template already adhering to these specs will be creat
154
178
  - `config.json` with default parameters for the plugins' configuration.
155
179
  - `README.md` describing the plugins' capabilities and usage.
156
180
  - `src/index.js` JS entry point.
181
+ - A plugin _may_ provide static plugin assets in a `plugin-assets` directory. (See [About Plugin Assets](#About-Plugin-Assets)
157
182
  - Plugin names are defined by the plugins' package name and therefore must obey npm [package name guidelines](https://docs.npmjs.com/package-name-guidelines):
158
183
  - choose a name that
159
184
  - is unique
@@ -167,7 +192,7 @@ to create your project, a template already adhering to these specs will be creat
167
192
  - Plugin dependencies have to be defined in the `package.json`.
168
193
  - `dependency`: all plugin specific dependencies NOT provided by the `@vcmap/ui`.
169
194
  - `peerDependency`: dependencies provided by the `@vcmap/ui`,
170
- - e.g. `@vcmap/core` or `@vcmap/ui` (see [About Peer Dependencies](#About_Peer_Dependencies) for more details)
195
+ - e.g. `@vcmap/core` or `@vcmap/ui` (see [About Peer Dependencies](#About-Peer-Dependencies) for more details)
171
196
  - `devDependency`: all dependencies only required for development, e.g. `eslint`.
172
197
  - Plugins can be published to NPM, but should contain both source and minified code
173
198
  to allow seamless integration into the [VC Map UI](https://github.com/virtualcitySYSTEMS/map-ui) environment.
@@ -179,22 +204,24 @@ For this reason the package.json of a plugin defines two exports:
179
204
  }
180
205
  ```
181
206
 
182
-
183
207
  ### Plugin Interface:
184
208
  Plugins must provide a function default export which returns an Object complying
185
- with the VC Map Plugin Interface describe below:
209
+ with the VC Map Plugin Interface describe below. This function is passed the current
210
+ configuration of the plugin as its first argument and the base URL (without the filename)
211
+ from which the plugin was loaded as its second argument.
186
212
 
187
213
  ```typescript
188
- declare interface VcsPlugin<T extends Object> {
214
+ declare interface VcsPlugin<T extends Object, S extends Object> {
189
215
  readonly name: string;
190
216
  readonly version: string;
191
- initialize(app: VcsUiApp):void;
192
- onVcsAppMounted(app: VcsUiApp):void;
193
- toJSON():T;
217
+ initialize(app: VcsUiApp, state?: S):Promise<void>;
218
+ onVcsAppMounted(app: VcsUiApp):Promise<void>;
219
+ getState():Promise<S>;
220
+ toJSON():Promise<T>;
194
221
  destroy():void;
195
222
  }
196
223
 
197
- declare function defaultExport<T extends Object>(config: T):VcsPlugin<T>;
224
+ declare function defaultExport<T extends Object, S extends Object>(config: T, baseUrl: string):VcsPlugin<T, S>;
198
225
  ```
199
226
 
200
227
  A Simple JavaScript implementation of this interface can be seen below::
@@ -204,22 +231,79 @@ A Simple JavaScript implementation of this interface can be seen below::
204
231
  * @param {PluginExampleConfig} config
205
232
  * @returns {VcsPlugin}
206
233
  */
207
- export default function defaultExport(config) {
234
+ export default function defaultExport(config, baseUrl) {
208
235
  return {
209
236
  get name() {
210
237
  return packageJSON.name;
211
238
  },
212
239
  get version() {
213
240
  return packageJSON.version;
241
+ },
242
+ async initialize (app, state) {
243
+ console.log('I was loaded from ', baseUrl);
214
244
  },
215
- initialize(app) {},
216
- onVcsAppMounted(app) {},
217
- toJSON() {},
245
+ async onVcsAppMounted(app) {},
246
+ async getState() { return {}; },
247
+ async toJSON() { return {}; },
218
248
  destroy() {},
219
249
  }
220
250
  }
221
251
  ```
222
252
 
253
+ ### About Plugin Assets
254
+ Plugin assets are considered to be static files, such as images, fonts etc. which shall be
255
+ access from within the plugin. Since plugins have no knowledge of _where_ they will
256
+ be deployed, the `@vcmap/ui` provides the `getPluginAssetUrl` helper function
257
+ which allows you to generate an asset URL at runtime.
258
+
259
+ Place all your assets into the `plugin-assets` directory in your plugin (top level). Your
260
+ plugin structure should look something like this:
261
+ ```
262
+ -| my-plugin/
263
+ ---| src/
264
+ -----| index.js
265
+ ---| plugin-assets/
266
+ -----| icon.png
267
+ ---| package.json
268
+ ```
269
+
270
+ To access the `icon.png` from within your code, you would do the following:
271
+ ```vue
272
+ <template>
273
+ <v-img
274
+ :src="icon"
275
+ alt="plugin-icon"
276
+ max-width="200"
277
+ />
278
+ </template>
279
+
280
+ <script>
281
+ import { inject } from 'vue';
282
+ import { getPluginAssetUrl } from '@vcmap/ui';
283
+ import { name } from '../package.json';
284
+
285
+ export const windowId = 'hello_world_window_id_plugin-cli';
286
+
287
+ export default {
288
+ name: 'HelloWorld',
289
+ components: { VcsButton },
290
+ setup() {
291
+ const app = inject('vcsApp');
292
+
293
+ return {
294
+ icon: getPluginAssetUrl(app, name, 'plugin-assets/icon.png'),
295
+ };
296
+ },
297
+ };
298
+ </script>
299
+ ```
300
+ You can of course, use `fetch` to retrieve assets in the same fashion. Should you
301
+ wish to use assets (such as images) in your _css_ make sure that they are embedded or
302
+ you will have to use an inline style & a bound vue property, since the helper
303
+ cannot handle css resources.
304
+
305
+ If you have to access assets _before_ your plugin is created (in the exported function of
306
+ your plugin code), you will have to use the `baseUrl` provided to you to generate the URL yourself.
223
307
 
224
308
  ## Notes on Developing
225
309
  To develop the plugin-cli, be sure to not `npm link` into plugins, since this will
@@ -8,8 +8,10 @@ stages:
8
8
  - build
9
9
  - test
10
10
  - bundle
11
+ - deploy
11
12
  - version
12
13
  - publish
14
+ - deployCluster
13
15
 
14
16
  .template: &job_definition
15
17
  only:
@@ -20,7 +22,6 @@ stages:
20
22
  build:
21
23
  <<: *job_definition
22
24
  script:
23
- - npm set registry 'http://npmregistry:4873'
24
25
  - npm ci
25
26
  before_script:
26
27
  - mkdir -p ~/.ssh
@@ -38,6 +39,12 @@ build:
38
39
  variables:
39
40
  GIT_STRATEGY: none
40
41
 
42
+ .staging_build_template: &staging_build_template
43
+ <<: *after_build_definition
44
+ except:
45
+ variables:
46
+ - $PUBLISH
47
+
41
48
  lint:
42
49
  <<: *after_build_definition
43
50
  stage: test
@@ -50,12 +57,74 @@ audit:
50
57
  script:
51
58
  - npm audit --production --audit-level=low
52
59
 
60
+ buildPreview:
61
+ <<: *staging_build_template
62
+ stage: bundle
63
+ script:
64
+ - npm run buildStagingApp
65
+
53
66
  bundle:
54
67
  <<: *after_build_definition
55
68
  stage: bundle
69
+ only:
70
+ variables:
71
+ - $PUBLISH
72
+ refs:
73
+ - /^(main|release-v.*)$/
56
74
  script:
57
75
  - npm run build
58
76
 
77
+ deployStaging:
78
+ <<: *staging_build_template
79
+ stage: deploy
80
+ environment:
81
+ name: staging/$CI_COMMIT_REF_SLUG
82
+ url: http://$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG.stagingcluster.intern.virtualcitysystems.de
83
+ on_stop: stopEnvironment
84
+ image:
85
+ name: gcr.io/kaniko-project/executor:debug
86
+ entrypoint: [ "" ]
87
+ script:
88
+ - /kaniko/executor --context dist/ --dockerfile build/staging/Dockerfile --destination $CI_REGISTRY_IMAGE/staging:$CI_COMMIT_REF_SLUG
89
+ before_script:
90
+ - mkdir -p /kaniko/.docker
91
+ - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
92
+
93
+ stopEnvironment:
94
+ stage: deploy
95
+ variables:
96
+ GIT_STRATEGY: none
97
+ image:
98
+ name: bitnami/kubectl:latest
99
+ entrypoint: [""]
100
+ tags:
101
+ - linux-2.0
102
+ script:
103
+ - echo "Stop environment staging/$CI_COMMIT_REF_NAME"
104
+ - echo "Delete namespace on k9s $CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG"
105
+ - kubectl config use-context vcsuite/cluster-management:agent
106
+ - kubectl delete namespace $CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG
107
+ when: manual
108
+ environment:
109
+ name: staging/$CI_COMMIT_REF_SLUG
110
+ action: stop
111
+
112
+ deployStagingCluster:
113
+ stage: deployCluster
114
+ except:
115
+ variables:
116
+ - $PUBLISH
117
+ inherit:
118
+ variables: false
119
+ variables:
120
+ STAGE_BRANCH: $CI_COMMIT_REF_SLUG
121
+ STAGE_PROJECT_NAME: $CI_PROJECT_PATH_SLUG
122
+ STAGE_REGISTRY_IMAGE: $CI_REGISTRY_IMAGE
123
+ STAGE_NAMESPACE: $CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG
124
+ trigger:
125
+ project: vcsuite/devops/manifests
126
+ branch: main
127
+
59
128
  version:
60
129
  <<: *after_build_definition
61
130
  stage: version
@@ -89,5 +158,5 @@ publish:
89
158
  variables:
90
159
  - $PUBLISH
91
160
  script:
92
- - npm config set '//npmregistry:4873/:_authToken' "${VERDACCIO_TOKEN}"
93
- - npm publish --registry http://npmregistry:4873
161
+ - npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
162
+ - npm publish --registry https://registry.npmjs.org --access public
@@ -0,0 +1,2 @@
1
+ FROM httpd:2-buster
2
+ COPY * /usr/local/apache2/htdocs/
@@ -1,9 +1,16 @@
1
1
  <template>
2
- <v-sheet>
2
+ <v-sheet class="hello-world">
3
3
  <v-card class="pa-2 ma-2">
4
4
  <v-container>
5
5
  <v-row class="justify-center mb-4">
6
- <h1>{{ $t('helloWorld.helloWorld')}}</h1>
6
+ <h1>{{ $t('helloWorld.helloWorld') }}</h1>
7
+ </v-row>
8
+ <v-row class="justify-center mb-4">
9
+ <v-img
10
+ :src="logoUrl"
11
+ alt="plugin-assets example"
12
+ max-width="200"
13
+ />
7
14
  </v-row>
8
15
  <v-row class="justify-center">
9
16
  <VcsButton
@@ -18,11 +25,17 @@
18
25
  </v-sheet>
19
26
  </template>
20
27
 
28
+ <style>
29
+ .hello-world {
30
+ background-color: aqua;
31
+ }
32
+ </style>
21
33
  <script>
22
- import { inject } from '@vue/composition-api';
23
- import { VcsButton } from '@vcmap/ui';
34
+ import { inject } from 'vue';
35
+ import { VcsButton, getPluginAssetUrl } from '@vcmap/ui';
36
+ import { name } from '../package.json';
24
37
 
25
- export const windowId = 'hello_world_window_id';
38
+ export const windowId = 'hello_world_window_id_plugin-cli';
26
39
 
27
40
  export default {
28
41
  name: 'HelloWorld',
@@ -34,6 +47,7 @@
34
47
  closeSelf() {
35
48
  app.windowManager.remove(windowId);
36
49
  },
50
+ logoUrl: getPluginAssetUrl(app, name, 'plugin-assets/vcs_logo.png'),
37
51
  };
38
52
  },
39
53
  };
@@ -4,18 +4,21 @@ import HelloWorld, { windowId } from './helloWorld.vue';
4
4
 
5
5
  /**
6
6
  * @param {T} config - the configuration of this plugin instance, passed in from the app.
7
+ * @param {string} baseUrl - the absolute URL from which the plugin was loaded (without filename, ending on /)
7
8
  * @returns {import("@vcmap/ui/src/vcsUiApp").VcsPlugin<T>}
8
9
  * @template {Object} T
10
+ * @template {Object} S
9
11
  */
10
- export default function(config) {
12
+ export default function(config, baseUrl) {
11
13
  return {
12
14
  get name() { return name; },
13
15
  get version() { return version; },
14
16
  /**
15
17
  * @param {import("@vcmap/ui").VcsUiApp} vcsUiApp
18
+ * @param {S=} state
16
19
  * @returns {Promise<void>}
17
20
  */
18
- initialize: async (vcsUiApp) => {
21
+ initialize: async (vcsUiApp, state) => {
19
22
  console.log('Called before loading the rest of the current context. Passed in the containing Vcs UI App ');
20
23
  },
21
24
  /**
@@ -34,22 +37,30 @@ export default function(config) {
34
37
  },
35
38
  }, name);
36
39
  },
40
+ /**
41
+ * @returns {Promise<S>}
42
+ */
43
+ getState: async () => {
44
+ console.log('Called when serializing this plugin instance');
45
+ return {};
46
+ },
37
47
  /**
38
48
  * @returns {Promise<T>}
39
49
  */
40
50
  toJSON: async () => {
41
51
  console.log('Called when serializing this plugin instance');
52
+ return {};
42
53
  },
43
54
  i18n: {
44
55
  en: {
45
56
  helloWorld: {
46
- helloWorld: 'Hello World',
57
+ helloWorld: '@vcmap/plugin-cli - Hello World',
47
58
  close: 'Close',
48
59
  },
49
60
  },
50
61
  de: {
51
62
  helloWorld: {
52
- helloWorld: 'Hallo Welt',
63
+ helloWorld: '@vcmap/plugin-cli - Hallo Welt',
53
64
  close: 'Schließen',
54
65
  },
55
66
  },
package/cli.js CHANGED
@@ -1,9 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import program from 'commander';
3
3
  import './src/defaultCommand.js';
4
- import { create, serve, build, pack, preview } from './index.js';
4
+ import {
5
+ create, serve, build, pack, preview,
6
+ } from './index.js';
5
7
  import { version } from './src/create.js';
6
8
  import setupMapUi from './src/setupMapUi.js';
9
+ import buildStagingApp from './src/buildStagingApp.js';
7
10
 
8
11
  program.version(version);
9
12
 
@@ -45,6 +48,11 @@ program
45
48
  .option('--watch', 'watch file changes')
46
49
  .safeAction(build);
47
50
 
51
+ program
52
+ .command('buildStagingApp')
53
+ .defaultOptions()
54
+ .safeAction(buildStagingApp);
55
+
48
56
  program
49
57
  .command('setup-map-ui')
50
58
  .safeAction(setupMapUi);
package/index.js CHANGED
@@ -3,5 +3,6 @@ export { default as serve } from './src/serve.js';
3
3
  export { default as build } from './src/build.js';
4
4
  export { default as pack } from './src/pack.js';
5
5
  export { default as preview } from './src/preview.js';
6
+ export { default as buildStagingApp } from './src/buildStagingApp.js';
6
7
  export { default as setupMapUi } from './src/setupMapUi.js';
7
8
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vcmap/plugin-cli",
3
- "version": "2.0.7",
3
+ "version": "2.0.10",
4
4
  "description": "A CLI to help develop and build plugins for the VC Map",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -34,15 +34,14 @@
34
34
  "prompts": "^2.4.1",
35
35
  "sass": "1.32.13",
36
36
  "semver": "^7.3.5",
37
- "unplugin-vue-components": "^0.19.6",
38
- "vinyl-fs": "^3.0.3",
39
- "vite": "^2.9.12",
37
+ "unplugin-vue-components": "^0.21.1",
38
+ "vite": "^2.9.14",
40
39
  "vite-plugin-vue2": "^2.0.1",
41
- "vue-template-compiler": "~2.6.14"
40
+ "vue-template-compiler": "~2.7.3"
42
41
  },
43
42
  "peerDependencies": {
44
- "@vcmap/ui": "^5.0.0-rc.9",
45
- "vue": "~2.6.14"
43
+ "@vcmap/ui": "^5.0.0-rc.11",
44
+ "vue": "~2.7.3"
46
45
  },
47
46
  "peerDependenciesMeta": {
48
47
  "@vcmap/ui": {
package/src/build.js CHANGED
@@ -12,6 +12,8 @@ import { getContext } from './context.js';
12
12
  * @typedef {Object} BuildOptions
13
13
  * @property {boolean} [development]
14
14
  * @property {boolean} [watch]
15
+ * @property {string} [outputPath] path where the plugin should be written, relative to the dist folder, default ''
16
+ * @property {boolean} [keepDistFolder] will not clear the dist folder if set to true
15
17
  */
16
18
 
17
19
  export function getDefaultConfig() {
@@ -62,8 +64,10 @@ export default async function buildModule(options) {
62
64
  const pluginName = await getPluginName();
63
65
  const libraryPaths = await getLibraryPaths(pluginName);
64
66
  const distPath = path.join(getContext(), 'dist');
65
- await fs.rm(distPath, { recursive: true, force: true });
66
- await fs.mkdir(distPath);
67
+ if (!options.keepDistFolder) {
68
+ await fs.rm(distPath, { recursive: true, force: true });
69
+ await fs.mkdir(distPath);
70
+ }
67
71
  const external = Object.keys(libraryPaths);
68
72
  const config = {
69
73
  ...getDefaultConfig(),
@@ -93,5 +97,5 @@ export default async function buildModule(options) {
93
97
  },
94
98
  };
95
99
  const { buildLibrary } = await import('@vcmap/ui/build/buildHelpers.js');
96
- await buildLibrary(config, '', 'index', '', true);
100
+ await buildLibrary(config, options.outputPath ?? '', 'index', '', true);
97
101
  }
@@ -0,0 +1,58 @@
1
+ import { cp, copyFile, writeFile, rm, mkdir } from 'fs/promises';
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ import { getContext, resolveContext } from './context.js';
5
+ import { getConfigJson } from './hostingHelpers.js';
6
+ import { getPluginName } from './packageJsonHelpers.js';
7
+ import buildModule, { getDefaultConfig } from './build.js';
8
+ import setupMapUi from './setupMapUi.js';
9
+
10
+
11
+ /**
12
+ * creates production preview application in the dist folder based on the @vcmap/ui default map configuration.
13
+ * @returns {Promise<void>}
14
+ */
15
+ export default async function buildStagingApp() {
16
+ const pluginName = await getPluginName();
17
+ const distPath = path.join(getContext(), 'dist');
18
+ // Clear dist folder
19
+ await rm(distPath, { recursive: true, force: true });
20
+ await mkdir(distPath);
21
+ await setupMapUi();
22
+ const { buildPluginsForPreview } = await import('@vcmap/ui/build/buildHelpers.js');
23
+ await buildPluginsForPreview(getDefaultConfig(), true);
24
+ await mkdir(path.join(distPath, 'plugins', pluginName), { recursive: true });
25
+
26
+ await buildModule({ outputPath: `plugins/${pluginName}`, keepDistFolder: true });
27
+
28
+ // copy assets folder if exists
29
+ if (fs.existsSync(resolveContext('plugin-assets'))) {
30
+ await cp(
31
+ resolveContext('plugin-assets'),
32
+ path.join(distPath, 'plugins', pluginName, 'plugin-assets'),
33
+ { recursive: true },
34
+ );
35
+ }
36
+
37
+ await copyFile(
38
+ path.join(getContext(), 'node_modules', '@vcmap', 'ui', 'dist', 'index.html'),
39
+ path.join(distPath, 'index.html'),
40
+ );
41
+ const config = await getConfigJson();
42
+ // update Entry
43
+ const pluginConfig = config.plugins.find(p => p.name === pluginName);
44
+ if (pluginConfig) {
45
+ pluginConfig.entry = `plugins/${pluginName}/index.js`;
46
+ }
47
+ await writeFile(path.join(distPath, 'map.config.json'), JSON.stringify(config, null, 2));
48
+ await cp(
49
+ path.join(getContext(), 'node_modules', '@vcmap', 'ui', 'dist', 'assets'),
50
+ path.join(distPath, 'assets'),
51
+ { recursive: true },
52
+ );
53
+ await cp(
54
+ path.join(getContext(), 'node_modules', '@vcmap', 'ui', 'config'),
55
+ path.join(distPath, 'config'),
56
+ { recursive: true },
57
+ );
58
+ }
package/src/create.js CHANGED
@@ -9,6 +9,7 @@ import { LicenseType, writeLicense } from './licenses.js';
9
9
  import { getDirname } from './hostingHelpers.js';
10
10
 
11
11
  export const { version, name } = JSON.parse(fs.readFileSync(path.join(getDirname(), '..', 'package.json')).toString());
12
+ const exec = util.promisify(childProcess.exec);
12
13
 
13
14
  /**
14
15
  * @typedef {Object} PluginTemplateOptions
@@ -19,11 +20,54 @@ export const { version, name } = JSON.parse(fs.readFileSync(path.join(getDirname
19
20
  * @property {string} author
20
21
  * @property {string} repository
21
22
  * @property {string} license
22
- * @property {string} registry
23
- * @property {boolean} addCiCd
24
23
  * @property {Array<string>} peerDeps
25
24
  */
26
25
 
26
+ /**
27
+ * @enum {number}
28
+ */
29
+ const DepType = {
30
+ DEP: 1,
31
+ PEER: 2,
32
+ DEV: 3,
33
+ };
34
+
35
+ /**
36
+ * @param {Array<string>} deps
37
+ * @param {DepType} type
38
+ * @param {string} pluginPath
39
+ * @returns {Promise<void>}
40
+ */
41
+ async function installDeps(deps, type, pluginPath) {
42
+ let save = '--save';
43
+ if (type === DepType.PEER) {
44
+ save = '--save-peer';
45
+ } else if (type === DepType.DEV) {
46
+ save = '--save-dev';
47
+ }
48
+ const installCmd = `npm i ${save} ${deps.join(' ')}`;
49
+ const { stdout, stderr } = await exec(installCmd, { cwd: pluginPath });
50
+ logger.log(stdout);
51
+ logger.error(stderr);
52
+ }
53
+
54
+ /**
55
+ * @param {Array<string>} deps
56
+ * @param {string} pluginPath
57
+ * @returns {Promise<void>}
58
+ */
59
+ async function setUiPeerDepVersions(deps, pluginPath) {
60
+ const uiPackageJsonContent = await fs.promises.readFile(
61
+ path.join(pluginPath, 'node_modules', '@vcmap', 'ui', 'package.json'),
62
+ );
63
+ const uiPackageJson = JSON.parse(uiPackageJsonContent);
64
+ deps.forEach((dep, index) => {
65
+ if (uiPackageJson.peerDependencies[dep]) {
66
+ deps[index] = `${dep}@${uiPackageJson.peerDependencies[dep]}`;
67
+ }
68
+ });
69
+ }
70
+
27
71
  /**
28
72
  * @param {PluginTemplateOptions} options
29
73
  */
@@ -100,7 +144,7 @@ async function createPluginTemplate(options) {
100
144
 
101
145
  const writeNpmrcPromise = fs.promises.writeFile(
102
146
  path.join(pluginPath, '.npmrc'),
103
- `registry=${options.registry}\n`,
147
+ 'registry=https://registry.npmjs.org\n',
104
148
  );
105
149
 
106
150
  const writeReadmePromise = fs.promises.writeFile(
@@ -137,21 +181,15 @@ async function createPluginTemplate(options) {
137
181
 
138
182
 
139
183
  logger.spin('installing dependencies... (this may take a while)');
140
- const exec = util.promisify(childProcess.exec);
141
184
  try {
142
- options.peerDeps.push('@vcmap/ui');
143
- const installCmd = `npm i --save-peer ${options.peerDeps.join(' ')}`;
144
- const { stdout, stderr } = await exec(installCmd, { cwd: pluginPath });
145
- logger.log(stdout);
146
- logger.error(stderr);
185
+ await installDeps(['@vcmap/ui'], DepType.PEER, pluginPath);
186
+ await setUiPeerDepVersions(options.peerDeps, pluginPath);
187
+ await installDeps(options.peerDeps, DepType.PEER, pluginPath);
147
188
  const devDeps = [`${name}@${version}`];
148
189
  if (installEsLint) {
149
190
  devDeps.push('@vcsuite/eslint-config');
150
191
  }
151
- const installDevCmd = `npm i --save-dev ${devDeps.join(' ')}`;
152
- const { stdout: stdoutDev, stderr: stderrDev } = await exec(installDevCmd, { cwd: pluginPath });
153
- logger.log(stdoutDev);
154
- logger.error(stderrDev);
192
+ await installDeps(devDeps, DepType.DEV, pluginPath);
155
193
  logger.success('installed dependencies');
156
194
  } catch (e) {
157
195
  logger.error(e);
@@ -170,14 +208,16 @@ export default async function create() {
170
208
  { title: 'pack', value: { pack: 'vcmplugin pack' }, selected: true },
171
209
  { title: 'start', value: { start: 'vcmplugin serve' }, selected: true },
172
210
  { title: 'preview', value: { preview: 'vcmplugin preview' }, selected: true },
211
+ { title: 'buildStagingApp', value: { buildStagingApp: 'vcmplugin buildStagingApp' }, selected: true },
173
212
  { title: 'lint', value: { lint: 'eslint "{src,tests}/**/*.{js,vue}"' }, selected: true },
174
213
  ];
175
214
 
176
215
  const peerDependencyChoices = [
177
216
  { title: '@vcmap/core', value: '@vcmap/core' },
178
217
  { title: '@vcmap/cesium', value: '@vcmap/cesium' },
179
- { title: 'ol', value: 'ol@~6.13.0' },
180
- { title: '@vue/composition-api', value: '@vue/composition-api@~1.4.5' },
218
+ { title: 'ol', value: 'ol' },
219
+ { title: 'vue', value: 'vue' },
220
+ { title: 'vuetify', value: 'vuetify' },
181
221
  ];
182
222
 
183
223
  const questions = [
@@ -239,12 +279,6 @@ export default async function create() {
239
279
  value: type,
240
280
  })),
241
281
  },
242
- {
243
- type: 'text',
244
- name: 'registry',
245
- message: 'Set default npm registry',
246
- initial: 'https://registry.npmjs.org',
247
- },
248
282
  {
249
283
  name: 'peerDeps',
250
284
  type: 'multiselect',
@@ -252,14 +286,6 @@ export default async function create() {
252
286
  choices: peerDependencyChoices,
253
287
  hint: '- Space to select. Enter to submit',
254
288
  },
255
- {
256
- type: 'toggle',
257
- name: 'addCiCd',
258
- message: 'Add default VCS gitlab ci/cd?',
259
- initial: false,
260
- active: 'yes',
261
- inactive: 'no',
262
- },
263
289
  ];
264
290
 
265
291
  const answers = await prompts(questions, { onCancel() { process.exit(0); } });
@@ -269,6 +269,16 @@ export async function getMapUiIndexHtml(production) {
269
269
  return buffer.toString();
270
270
  }
271
271
 
272
+ /**
273
+ * @param {Express} app
274
+ * @param {string} base
275
+ */
276
+ export function addPluginAssets(app, base) {
277
+ app.use(`/${base}/plugin-assets*`, (req, res) => {
278
+ res.redirect(308, req.originalUrl.replace(`/${base}`, ''));
279
+ });
280
+ }
281
+
272
282
  /**
273
283
  * @param {Express} app
274
284
  * @param {import("vite").ViteDevServer} server
package/src/pack.js CHANGED
@@ -1,40 +1,10 @@
1
- import { Transform } from 'stream';
2
1
  import fs from 'fs';
3
- import vinylFs from 'vinyl-fs';
4
2
  import archiver from 'archiver';
5
3
  import { logger } from '@vcsuite/cli-logger';
6
4
  import { getPluginName } from './packageJsonHelpers.js';
7
- import { resolveContext, getContext } from './context.js';
5
+ import { resolveContext } from './context.js';
8
6
  import build from './build.js';
9
7
 
10
- /**
11
- * @param {string} name
12
- * @returns {Promise<void>}
13
- */
14
- function replaceAssets(name) {
15
- const replaceTransform = new Transform({
16
- objectMode: true,
17
- transform(data, encoding, callback) {
18
- data.contents = Buffer.from(String(data.contents)
19
- .replace(/\.?\/?(plugin-assets)\//g, `plugins/${name}/$1/`));
20
-
21
- callback(null, data);
22
- },
23
- });
24
-
25
- const context = getContext();
26
- const stream = vinylFs.src([resolveContext('dist', '*')], {
27
- cwd: context,
28
- allowEmpty: false,
29
- })
30
- .pipe(replaceTransform)
31
- .pipe(vinylFs.dest(resolveContext('dist')));
32
-
33
- return new Promise((resolve, reject) => {
34
- stream.on('finish', () => { resolve(); });
35
- stream.on('error', reject);
36
- });
37
- }
38
8
 
39
9
  /**
40
10
  * @returns {Promise<void>}
@@ -89,7 +59,6 @@ function zip(name) {
89
59
  ['README.md'],
90
60
  ['config.json'],
91
61
  ['dist', 'index.js'],
92
- ['dist', 'style.css'],
93
62
  ].forEach((fileArray) => {
94
63
  const file = resolveContext(...fileArray);
95
64
  if (fs.existsSync(file)) {
@@ -114,8 +83,6 @@ export default async function pack() {
114
83
  const pluginName = await getPluginName();
115
84
  logger.spin(`building plugin: ${pluginName}`);
116
85
  await build({});
117
- await replaceAssets(pluginName);
118
- logger.debug('fixed asset paths');
119
86
  await ensureConfigJson();
120
87
  logger.debug('ensuring config.json');
121
88
  await zip(pluginName);
package/src/preview.js CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  addConfigRoute,
8
8
  addIndexRoute,
9
9
  addMapConfigRoute,
10
+ addPluginAssets,
10
11
  checkReservedDirectories,
11
12
  createConfigJsonReloadPlugin,
12
13
  printVcmapUiVersion, resolveMapUi,
@@ -83,6 +84,7 @@ export default async function preview(options) {
83
84
 
84
85
  addMapConfigRoute(app, options.vcm ? `${options.vcm}/map.config.json` : null, options.auth, options.config, true);
85
86
  addIndexRoute(app, server, true, options.vcm, options.auth);
87
+ addPluginAssets(app, 'dist');
86
88
 
87
89
  if (!options.vcm) {
88
90
  logger.spin('compiling preview');
package/src/serve.js CHANGED
@@ -10,7 +10,7 @@ import { getContext } from './context.js';
10
10
  import {
11
11
  addConfigRoute,
12
12
  addIndexRoute,
13
- addMapConfigRoute,
13
+ addMapConfigRoute, addPluginAssets,
14
14
  checkReservedDirectories,
15
15
  createConfigJsonReloadPlugin,
16
16
  printVcmapUiVersion,
@@ -114,6 +114,7 @@ export default async function serve(options) {
114
114
 
115
115
  addMapConfigRoute(app, options.mapConfig, options.auth, options.config);
116
116
  addIndexRoute(app, server);
117
+ addPluginAssets(app, 'src');
117
118
  await addConfigRoute(app, options.auth, options.config);
118
119
 
119
120
  app.use(server.middlewares);
@@ -1,2 +0,0 @@
1
- node_modules/
2
- dist/