@vcmap/plugin-cli 1.0.0 → 2.0.1

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/README.md CHANGED
@@ -1,5 +1,7 @@
1
- # @vcmplugin/plugin-cli
2
- The `vcmplugin` cli helps develop and build plugins for the **vcMAP**.
1
+ # @vcmap/plugin-cli
2
+ > Part of the [VC Map Project](https://github.com/virtualcitySYSTEMS/map-ui)
3
+
4
+ The `vcmplugin` cli helps develop and build plugins for the **VC Map**.
3
5
 
4
6
  For more information on plugin development refer to [map plugin examples](https://github.com/virtualcitySYSTEMS/map-plugin-examples),
5
7
  which provides documentations and a tutorial on plugin development.
@@ -12,7 +14,8 @@ which provides documentations and a tutorial on plugin development.
12
14
 
13
15
  ## Prerequisite
14
16
 
15
- You need [nodejs](https://nodejs.org/en/) and npm installed on your system to use this tool.
17
+ You need [nodejs](https://nodejs.org/en/) 16 and npm installed on your system
18
+ to use this tool.
16
19
 
17
20
  ## Installation
18
21
  To install in your project:
@@ -33,33 +36,48 @@ To create a new plugin template, run the following within your projects root:
33
36
  ```
34
37
  vcmplugin create
35
38
  ```
36
- This will open a command prompt helping you to create the basic [structure of a plugin](https://github.com/virtualcitySYSTEMS/map-plugin-examples/doc/VCM_Plugin.md#2-structure-of-a-plugin).
37
-
39
+ This will open a command prompt helping you to create the basic [structure of a plugin](https://github.com/virtualcitySYSTEMS/map-plugin-examples/blob/main/doc/VCM_Plugin.md#2-structure-of-a-plugin).
40
+ Be sure to check out the [peer dependecy section](#about_peer_dependencies) as well.
38
41
 
39
42
  ### 2. Serving a plugin for development
40
43
 
41
- To serve your project, run the following within your projects root:
44
+ To serve your plugin in dev mode, run the following within your projects root:
42
45
  ```
43
- vcmplugin serve --vcm <url|directory>
46
+ vcmplugin serve
44
47
  ```
45
- This will launch a dev server at localhost:8080 using the specified VC MAP application as its base.
46
- You can either specify a directory or a URL to an application.
48
+ The dev mode gives you complete debug information on all integrated libraries (@vcmap/core, ol etc.)
49
+ By default this command will launch a dev server at localhost:8008 using
50
+ the @vcmap/ui peer dependency package of your plugin as its base.
51
+ You can provide an alternate map config if you wish.
47
52
 
48
- ```bash
49
- # using a directory
50
- vcmplugin serve --vcm /home/vcs/virtualcityMAP
51
- # using a URL
52
- vcmplugin serve --vcm https://berlin.virtualcitymap.de
53
+ This is the dev mode, only _your_
54
+ plugin will be served. Any other plugins in the config will be stripped. To view how
55
+ your plugin integrates with others, use the `preview` command.
56
+
57
+ ### 3. Serving a plugin for integration
58
+
59
+ To serve your plugin in preview mode, run the following within your projects root:
60
+ ```
61
+ vcmplugin preview
53
62
  ```
54
63
 
55
- ### 3. Building a plugin
64
+ The preview mode allows you to view your plugin _in its destined environment_.
65
+ You can see how it interacts with other plugins & other customizations applied to a map.
66
+ Preview will `build` your plugin continuously and serve the production ready
67
+ code using a base application.
68
+ By default, this will launch a dev server at localhost:5005 using the @vcmap/ui package
69
+ as its base. Alternatively you can provide a URL to a hosted VC Map application
70
+ and use said application as its base instead.
71
+
72
+ ### 4. Building a plugin
56
73
 
57
74
  To build your project, run the following from within your projects root:
58
75
  ```bash
59
76
  vcmplugin build
60
77
  ```
78
+ This will build your application and place it in the `dist` directory.
61
79
 
62
- ### 4. Integrating a plugin in a productive VC MAP
80
+ ### 5. Integrating a plugin in a productive VC MAP
63
81
 
64
82
  To pack your project for productive use, run the following from within your projects root:
65
83
  ```bash
@@ -67,7 +85,57 @@ vcmplugin pack
67
85
  ```
68
86
 
69
87
  This will create a folder `dist` with a zip file containing your bundled code and assets.
70
- To use the plugin productively in a hosted map, unzip this file on your server to `{vcm-root}/plugins` and add an entry to your VC MAP [config](#2-config) plugins section.
88
+ To use the plugin productively in a hosted map,
89
+ unzip this file on your server to `{vcm-root}/plugins` and add
90
+ an entry to your VC MAP [config](#2-config) plugins section. This zip file can also be unzipped
91
+ in the VC Publishers `plugins` public directory.
92
+
93
+ ## About Peer Dependencies
94
+ The @vcmap/ui uses some _very large libraries_, notably `CesiumJS`. To reduce the amount
95
+ of traffic generated for loading plugins, all large libraries (see the list below),
96
+ are _provided_ in production (instead of bundling them into every plugin). This a) guarantees
97
+ a certain amount of type safety (using the @vcsuite/check parameter assertation library for instance),
98
+ b) reduces the amount of traffic required to load an application and c) leverages browser
99
+ caching more readily.
100
+
101
+ The following libraries are provided by the @vcmap/ui in a deployed application. You should define these
102
+ as peer dependencies if you use them in your plugin:
103
+ - @vcmap/core
104
+ - @vcmap/cesium
105
+ - ol
106
+ - @vcsuite/ui-components
107
+ - vue
108
+ - @vue/composition-api
109
+ - vuetify
110
+
111
+ During the build step, these libraries are automatically externalized by the vcmplugin-cli and in
112
+ production all plugins & the map core _share_ the same cesium library.
113
+
114
+ But, to make this work, it is important to define these dependencies as _peer dependencies_ of
115
+ a plugin and _that the provided index files_ are used (over directly importing from the source file).
116
+
117
+ For instance:
118
+ ```js
119
+ import Cartesian3 from '@vcmap/cesium/Source/Core/Cartesian3.js';
120
+ ```
121
+
122
+ should be rewritten to:
123
+ ```js
124
+ import { Cartesian3 } from '@vcmap/cesium';
125
+ ```
126
+
127
+ ### What about openlayers?
128
+ openlayers provides a special case, since its modules do not provide a _flat_ namespace.
129
+ To circumvent this limitation, _the @vcmap/ui provides a flat namespaced ol.js_ and a mechanic
130
+ to rewrite openlayers imports. This is automatically applied by the `@vcmap/rollup-plugin-vcs-ol`
131
+ used by the vcmplugin-cli build step. So openlayers imports can be written as:
132
+ ```js
133
+ import Feature from 'ol/Feature.js';
134
+ ```
135
+ or
136
+ ```js
137
+ import { Feature } from 'ol';
138
+ ```
71
139
 
72
140
  ## Non-Global CLI & npm run
73
141
  If you only use the `vcmplugin-cli` as a package dependency, you must add the above scripts to
@@ -81,14 +149,7 @@ the `package.json` and use `npm run` to execute:
81
149
  "serve": "vcmplugin serve --vcm ./vcm"
82
150
  },
83
151
  "devDependencies": {
84
- "vcmplugin-cli": "^0.1.1"
152
+ "@vcmap/plugin-cli": "^0.1.1"
85
153
  }
86
154
  }
87
155
  ```
88
-
89
- ## Considerations
90
- The legacy case was not as strict regarding the projects `package.json`. This approach relies
91
- more heavily on a) the precense of a `package.json` and b) the validity of said package.json. For
92
- instance the plugin name is directly derived from the `name` field in the package.json as is the
93
- entry point from `main`. You can still provide `name` as a CLI argument and `src/index.js` is still
94
- used, if `main` is missing from the `package.json`. This is do to change.
@@ -0,0 +1,81 @@
1
+ <!DOCTYPE html>
2
+ <html class="vcs-ui" lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width,initial-scale=1.0" />
6
+ <link
7
+ rel="stylesheet"
8
+ href="https://fonts.googleapis.com/css?family=Titillium+Web"
9
+ />
10
+ </head>
11
+ <body style="height: 100vH;">
12
+ <noscript>
13
+ <strong>...</strong>
14
+ </noscript>
15
+ <div id="app">
16
+ <div id="loading-wrapper">
17
+ <div id="loading-text">LOADING</div>
18
+ <div id="loading-content"></div>
19
+ </div>
20
+ </div>
21
+ <style>
22
+ #loading-wrapper {
23
+ position: fixed;
24
+ width: 100%;
25
+ height: 100%;
26
+ left: 0;
27
+ top: 0;
28
+ }
29
+
30
+ #loading-text {
31
+ display: block;
32
+ position: absolute;
33
+ top: 50%;
34
+ left: 50%;
35
+ color: #409d76;
36
+ width: 100px;
37
+ height: 30px;
38
+ margin: -7px 0 0 -45px;
39
+ text-align: center;
40
+ font-family: 'PT Sans Narrow', sans-serif;
41
+ font-size: 20px;
42
+ }
43
+
44
+ #loading-content {
45
+ display: block;
46
+ position: relative;
47
+ left: 50%;
48
+ top: 50%;
49
+ width: 170px;
50
+ height: 170px;
51
+ margin: -85px 0 0 -85px;
52
+ }
53
+
54
+ #loading-content {
55
+ border: 3px solid transparent;
56
+ border-top-color: #409d76;
57
+ border-bottom-color: #409d76;
58
+ border-radius: 50%;
59
+ -webkit-animation: loader 2s linear infinite;
60
+ -moz-animation: loader 2s linear infinite;
61
+ -o-animation: loader 2s linear infinite;
62
+ animation: loader 2s linear infinite;
63
+ }
64
+
65
+ @keyframes loader {
66
+ 0% {
67
+ -webkit-transform: rotate(0deg);
68
+ -ms-transform: rotate(0deg);
69
+ transform: rotate(0deg);
70
+ }
71
+
72
+ 100% {
73
+ -webkit-transform: rotate(360deg);
74
+ -ms-transform: rotate(360deg);
75
+ transform: rotate(360deg);
76
+ }
77
+ }
78
+ </style>
79
+ <script type="module" src="./node_modules/@vcmap/ui/start.js"></script>
80
+ </body>
81
+ </html>
package/cli.js CHANGED
@@ -1,28 +1,26 @@
1
1
  #!/usr/bin/env node
2
- const program = require('commander');
3
- require('./src/defaultCommand');
4
- const { version } = require('./package.json');
5
- const { create, serve, build, pack } = require('./index');
2
+ import program from 'commander';
3
+ import './src/defaultCommand.js';
4
+ import { create, serve, build, pack, preview } from './index.js';
5
+ import { version } from './src/create.js';
6
6
 
7
7
  program.version(version);
8
8
 
9
9
  program
10
10
  .command('create')
11
- .action(create);
11
+ .defaultOptions()
12
+ .safeAction(create);
12
13
 
13
14
  program
14
15
  .command('pack')
15
- .defaultBuildOptions()
16
- .action(pack);
16
+ .defaultOptions()
17
+ .safeAction(pack);
17
18
 
18
19
  program
19
- .command('serve')
20
+ .command('preview')
20
21
  .defaultOptions()
21
- .option('-p, --port [port]', 'the port to listen on', /\d+/, '8080')
22
- .option('--vcm [dir]', 'the directory path or URL to a virtualcityMAP application', './vcm')
23
- .option('--index [index.html]', 'the filename of the index.html to download. only used if vcm is a hosted application', 'index.html')
24
- .option('--auth <user:password>', 'an optional auth to append to proxy requests')
25
- .option('-c, --config <config>', 'a config override to not use the default plugin config')
22
+ .defaultServeOptions()
23
+ .option('--vcm [url]', 'URL to a virtualcityMAP application', val => val.replace(/\/$/, ''))
26
24
  .option('--proxyRoute <route>', 'a route to proxy as well (e.g. if you have additional proxies on your server)', (val, prev) => {
27
25
  if (!prev) {
28
26
  return [val];
@@ -30,13 +28,20 @@ program
30
28
  prev.push(val);
31
29
  return prev;
32
30
  }, [])
33
- .action(serve);
31
+ .safeAction(preview);
32
+
33
+ program
34
+ .command('serve')
35
+ .defaultOptions()
36
+ .defaultServeOptions()
37
+ .option('--mapConfig [config]', 'an optional map config (either file or URL) to use')
38
+ .safeAction(serve);
34
39
 
35
40
  program
36
41
  .command('build')
37
- .defaultBuildOptions()
42
+ .defaultOptions()
38
43
  .option('--development', 'set mode to development')
39
44
  .option('--watch', 'watch file changes')
40
- .action(build);
45
+ .safeAction(build);
41
46
 
42
47
  program.parse(process.argv);
package/index.js CHANGED
@@ -1,12 +1,5 @@
1
- const create = require('./src/create');
2
- const serve = require('./src/serve');
3
- const build = require('./src/build');
4
- const pack = require('./src/pack');
5
-
6
-
7
- module.exports = {
8
- create,
9
- serve,
10
- build,
11
- pack,
12
- };
1
+ export { default as create } from './src/create.js';
2
+ export { default as serve } from './src/serve.js';
3
+ export { default as build } from './src/build.js';
4
+ export { default as pack } from './src/pack.js';
5
+ export { default as preview } from './src/preview.js';
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@vcmap/plugin-cli",
3
- "version": "1.0.0",
4
- "description": "A CLI to help develop and build plugins for the virtualcityMAP",
3
+ "version": "2.0.1",
4
+ "description": "A CLI to help develop and build plugins for the VC Map",
5
5
  "main": "index.js",
6
+ "type": "module",
6
7
  "bin": {
7
8
  "vcmplugin": "cli.js"
8
9
  },
@@ -13,6 +14,7 @@
13
14
  "author": "Ben Kuster <bkuster@virtualcitysystems.de>",
14
15
  "license": "MIT",
15
16
  "files": [
17
+ "assets/",
16
18
  "src/",
17
19
  "cli.js",
18
20
  "README.md",
@@ -20,35 +22,35 @@
20
22
  "LICENSE.md"
21
23
  ],
22
24
  "dependencies": {
23
- "@babel/core": "^7.10.2",
24
- "@babel/runtime": "^7.13.10",
25
+ "@vcmap/rollup-plugin-vcs-ol": "^1.0.1",
25
26
  "@vcsuite/cli-logger": "^1.0.0",
26
- "@vue/babel-preset-app": "^4.5.4",
27
27
  "archiver": "^5.0.0",
28
- "autoprefixer": "^9.8.6",
29
- "babel-loader": "^8.1.0",
30
28
  "commander": "^6.0.0",
31
- "css-loader": "^3.6.0",
32
- "postcss-loader": "^3.0.0",
29
+ "express": "^4.17.3",
33
30
  "prompts": "^2.4.1",
31
+ "sass": "1.32.13",
34
32
  "semver": "^7.3.5",
35
- "terser-webpack-plugin": "^5.1.1",
36
- "url-loader": "^4.1.0",
33
+ "unplugin-vue-components": "^0.17.21",
37
34
  "vinyl-fs": "^3.0.3",
38
- "vue": "^2.6.12",
39
- "vue-loader": "^15.9.6",
40
- "vue-template-compiler": "^2.6.12",
41
- "webpack": "^5.36.2",
42
- "webpack-dev-server": "^3.11.2"
35
+ "vite": "^2.4.4",
36
+ "vite-plugin-vue2": "^1.7.3",
37
+ "vue-template-compiler": "^2.6.14"
43
38
  },
44
39
  "devDependencies": {
45
- "@vcsuite/eslint-config": "^1.0.0",
46
- "eslint": "^7.25.0"
40
+ "@vcsuite/eslint-config": "^2.1.1",
41
+ "eslint": "^8.9.0"
47
42
  },
48
43
  "eslintIgnore": [
49
44
  "node_modules"
50
45
  ],
51
46
  "eslintConfig": {
52
- "extends": "@vcsuite/eslint-config/node"
47
+ "extends": "@vcsuite/eslint-config/node",
48
+ "parserOptions": {
49
+ "ecmaVersion": 2020
50
+ }
51
+ },
52
+ "engines": {
53
+ "node": "^16.14.0",
54
+ "npm": "^8.3.0"
53
55
  }
54
56
  }
package/src/build.js CHANGED
@@ -1,32 +1,118 @@
1
- const webpack = require('webpack');
2
- const { logger } = require('@vcsuite/cli-logger');
3
- const { getPluginName } = require('./packageJsonHelpers');
4
- const { getProdWebpackConfig } = require('./getWebpackConfig');
5
-
6
- async function build(options) {
7
- function webpackHandler(err, stats) {
8
- if (err) {
9
- logger.error(err);
10
- } else if (stats.hasErrors()) {
11
- logger.error(stats.compilation.errors);
12
- } else {
13
- logger.success(`built ${options.pluginName}`);
14
- }
15
- logger.stopSpinner();
16
- }
1
+ import path from 'path';
2
+ import { build } from 'vite';
3
+ import { createVuePlugin } from 'vite-plugin-vue2';
4
+ import vcsOl from '@vcmap/rollup-plugin-vcs-ol';
5
+ import { logger } from '@vcsuite/cli-logger';
6
+ import { VuetifyResolver } from 'unplugin-vue-components/dist/resolvers.js';
7
+ import Components from 'unplugin-vue-components/dist/vite.js';
8
+ import { getPluginEntry, getPluginName } from './packageJsonHelpers.js';
17
9
 
18
- options.pluginName = options.pluginName || await getPluginName();
19
- logger.spin(`compiling ${options.pluginName}`);
20
- options.mode = options.development ? 'development' : 'production';
10
+ /**
11
+ * @typedef {Object} BuildOptions
12
+ * @property {boolean} [development]
13
+ * @property {boolean} [watch]
14
+ */
21
15
 
22
- const config = await getProdWebpackConfig(options);
23
- const compiler = webpack(config);
16
+ /**
17
+ * @param {string} pluginName
18
+ * @returns {Object<string, string>}
19
+ */
20
+ export function getLibraryPaths(pluginName) {
21
+ const pluginPath = path.join('plugins', ...pluginName.split('/'));
24
22
 
25
- if (options.watch) {
26
- compiler.watch({}, webpackHandler);
27
- } else {
28
- compiler.run(webpackHandler);
29
- }
23
+ const libraries = {
24
+ vue: 'vue',
25
+ '@vue/composition-api': 'vue-composition-api',
26
+ '@vcmap/cesium': 'cesium',
27
+ ol: 'ol',
28
+ '@vcmap/core': 'core',
29
+ 'vuetify/lib': 'vuetify',
30
+ '@vcsuite/ui-components': 'uicomponents',
31
+ '@vcmap/ui': 'ui',
32
+ };
33
+
34
+ Object.entries(libraries).forEach(([library, assetName]) => {
35
+ const assetPath = path.join('assets', `${assetName}.js`);
36
+
37
+ libraries[library] = path.relative(pluginPath, assetPath);
38
+ });
39
+ return libraries;
30
40
  }
31
41
 
32
- module.exports = build;
42
+ /**
43
+ * @param {BuildOptions} options
44
+ * @param {boolean} [preview]
45
+ * @returns {Promise<void>}
46
+ */
47
+ export default async function buildModule(options, preview) {
48
+ const entry = await getPluginEntry();
49
+ if (path.relative('src', entry).startsWith('.')) {
50
+ logger.warning(`detected irregular entry ${entry}`);
51
+ logger.warning('vuetify component resolution expects source files to be within "src"');
52
+ }
53
+
54
+ const pluginName = await getPluginName();
55
+ const libraries = getLibraryPaths(pluginName);
56
+
57
+ await build({
58
+ publicDir: false,
59
+ plugins: [
60
+ createVuePlugin(),
61
+ Components({
62
+ resolvers: [
63
+ VuetifyResolver(),
64
+ ],
65
+ dirs: ['./src'],
66
+ include: [],
67
+ exclude: [],
68
+ }),
69
+ vcsOl(),
70
+ {
71
+ name: 'pluginCssLoaderPrefix',
72
+ generateBundle(opts, bundle) {
73
+ const indexJs = bundle['index.js'];
74
+ if (indexJs && indexJs.code) {
75
+ const resource = preview ?
76
+ './dist/style.css' :
77
+ `./plugins/${pluginName}/style.css`;
78
+
79
+ indexJs.code = `
80
+ function loadCss(href) {
81
+ return new Promise((resolve, reject) => {
82
+ const elem = document.createElement('link');
83
+ elem.rel = 'stylesheet';
84
+ elem.href = href;
85
+ elem.defer = false;
86
+ elem.async = false;
87
+ elem.onload = resolve;
88
+ elem.onerror = reject;
89
+ document.head.appendChild(elem);
90
+ });
91
+ }
92
+ await loadCss('${resource}');
93
+ ${indexJs.code}
94
+ `;
95
+ }
96
+ },
97
+ },
98
+ ],
99
+ esbuild: {
100
+ minify: !options.development,
101
+ },
102
+ build: {
103
+ emptyOutDir: true,
104
+ lib: {
105
+ entry,
106
+ formats: ['es'],
107
+ fileName: () => 'index.js',
108
+ },
109
+ rollupOptions: {
110
+ external: Object.keys(libraries),
111
+ output: {
112
+ paths: libraries,
113
+ },
114
+ },
115
+ watch: options.watch ? {} : null,
116
+ },
117
+ });
118
+ }
package/src/context.js CHANGED
@@ -1,25 +1,22 @@
1
- const path = require('path');
1
+ import path from 'path';
2
+ import fs from 'fs';
2
3
 
3
4
  let context = null;
4
5
 
5
- function setContext(c) {
6
+ export function setContext(c) {
6
7
  if (context) {
7
8
  throw new Error('cannot set context twice');
8
9
  }
9
- context = c;
10
+ if (!fs.existsSync(c) || !fs.statSync(c).isDirectory()) {
11
+ throw new Error('context must be an existing directory');
12
+ }
13
+ context = path.resolve(c);
10
14
  }
11
15
 
12
- function getContext() {
16
+ export function getContext() {
13
17
  return context || process.cwd();
14
18
  }
15
19
 
16
- function resolveContext(...dir) {
20
+ export function resolveContext(...dir) {
17
21
  return path.join(getContext(), ...dir);
18
22
  }
19
-
20
-
21
- module.exports = {
22
- getContext,
23
- resolveContext,
24
- setContext,
25
- };