@vcmap/plugin-cli 1.1.1 → 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/src/preview.js ADDED
@@ -0,0 +1,94 @@
1
+ import path from 'path';
2
+ import { createServer } from 'vite';
3
+ import express from 'express';
4
+ import { logger } from '@vcsuite/cli-logger';
5
+ import {
6
+ addIndexRoute,
7
+ addMapConfigRoute,
8
+ checkReservedDirectories,
9
+ createConfigJsonReloadPlugin,
10
+ printVcmapUiVersion,
11
+ } from './hostingHelpers.js';
12
+ import build, { getLibraryPaths } from './build.js';
13
+ import { getContext } from './context.js';
14
+
15
+ /**
16
+ * @typedef {HostingOptions} PreviewOptions
17
+ * @property {string} [vcm]
18
+ */
19
+
20
+ /**
21
+ * @param {Object<string, string>} alias
22
+ * @param {Object<string, string>} libraryPaths
23
+ */
24
+ function setAliases(alias, libraryPaths) {
25
+ Object.values(libraryPaths).forEach((entry) => {
26
+ alias[entry] = entry.replace(/(..\/)*assets/, '/assets');
27
+ });
28
+ }
29
+
30
+ /**
31
+ * @param {string} [hostedVcm]
32
+ * @param {boolean} [https]
33
+ * @returns {Promise<import("vite").InlineConfig>}
34
+ */
35
+ async function getServerOptions(hostedVcm, https) {
36
+ let proxy;
37
+ const normalLibraries = getLibraryPaths('normal');
38
+ const scopedLibraries = getLibraryPaths('@scoped/plugin');
39
+ const alias = {};
40
+ setAliases(alias, normalLibraries);
41
+ setAliases(alias, scopedLibraries);
42
+
43
+ if (hostedVcm) {
44
+ proxy = {
45
+ '^/style.css': hostedVcm,
46
+ '^/assets': hostedVcm,
47
+ '^/plugins': hostedVcm,
48
+ };
49
+ }
50
+
51
+ return {
52
+ publicDir: false,
53
+ plugins: [
54
+ createConfigJsonReloadPlugin(),
55
+ ],
56
+ resolve: {
57
+ alias,
58
+ },
59
+ server: {
60
+ middlewareMode: 'html',
61
+ proxy,
62
+ https,
63
+ },
64
+ };
65
+ }
66
+
67
+ /**
68
+ * @param {PreviewOptions} options
69
+ * @returns {Promise<void>}
70
+ */
71
+ export default async function preview(options) {
72
+ if (!options.vcm) {
73
+ await printVcmapUiVersion();
74
+ }
75
+ checkReservedDirectories();
76
+ build({ development: false, watch: true }, true);
77
+ const app = express();
78
+ logger.info('Starting preview server...');
79
+ const server = await createServer(await getServerOptions(options.vcm, options.https));
80
+
81
+ addMapConfigRoute(app, options.vcm ? `${options.vcm}/map.config.json` : null, options.auth, options.config, true);
82
+ addIndexRoute(app, server, true, options.vcm, options.auth);
83
+
84
+ if (!options.vcm) {
85
+ app.use('/assets', express.static(path.join(getContext(), 'node_modules', '@vcmap', 'ui', 'dist', 'assets')));
86
+ app.use('/plugins', express.static(path.join(getContext(), 'node_modules', '@vcmap', 'ui', 'dist', 'plugins')));
87
+ }
88
+
89
+ app.use(server.middlewares);
90
+
91
+ const port = options.port || 5005;
92
+ await app.listen(port);
93
+ logger.info(`Server running on port ${port}`);
94
+ }
package/src/serve.js CHANGED
@@ -1,204 +1,80 @@
1
- const path = require('path');
2
- const https = require('https');
3
- const http = require('http');
4
- const { URL } = require('url');
5
- const fs = require('fs');
6
- const WebpackDevServer = require('webpack-dev-server');
7
- const webpack = require('webpack');
8
- const { logger } = require('@vcsuite/cli-logger');
9
- const { getContext, resolveContext } = require('./context');
10
- const { getDevWebpackConfig } = require('./getWebpackConfig');
11
- const { getPluginName } = require('./packageJsonHelpers');
12
-
13
- function httpGet(stringUrl, auth, handler) {
14
- const url = new URL(stringUrl);
15
- const options = {
16
- hostname: url.hostname,
17
- port: url.port,
18
- path: url.pathname,
19
- };
20
-
21
- if (auth) {
22
- options.headers = { Authorization: `Basic ${Buffer.from(auth).toString('base64')}` };
23
- }
24
- if (url.protocol === 'https:') {
25
- https.get(options, handler);
26
- } else {
27
- http.get(options, handler);
28
- }
29
- }
30
-
31
- function getIndexHtml(stringUrl, fileName, auth) {
32
- return new Promise((resolve, reject) => {
33
- httpGet(stringUrl, auth, (res) => {
34
- if (res.statusCode >= 400) {
35
- logger.error('got status code: ', res.statusCode);
36
- reject(new Error(`StatusCode ${res.statusCode}`));
37
- }
38
- const write = fs.createWriteStream(resolveContext(fileName));
39
- write.on('finish', resolve);
40
- write.on('error', (err) => {
41
- reject(err);
42
- });
43
- res.pipe(write);
44
- });
45
- });
46
- }
47
-
48
- let configJson = null;
1
+ import fs from 'fs';
2
+ import { createServer } from 'vite';
3
+ import { createVuePlugin } from 'vite-plugin-vue2';
4
+ import express from 'express';
5
+ import { logger } from '@vcsuite/cli-logger';
6
+ import path from 'path';
7
+ import { VuetifyResolver } from 'unplugin-vue-components/dist/resolvers.js';
8
+ import Components from 'unplugin-vue-components/dist/vite.js';
9
+ import { getContext } from './context.js';
10
+ import {
11
+ addIndexRoute,
12
+ addMapConfigRoute,
13
+ checkReservedDirectories,
14
+ createConfigJsonReloadPlugin,
15
+ printVcmapUiVersion,
16
+ } from './hostingHelpers.js';
49
17
 
50
18
  /**
51
- * @param {string} fileName
52
- * @returns {Promise<Object>}
19
+ * @typedef {HostingOptions} ServeOptions
20
+ * @property {string} [mapConfig] - an filename or URL to a map config
53
21
  */
54
- async function readConfigJson(fileName) {
55
- const configFileName = fileName || resolveContext('config.json');
56
- let config = {};
57
- if (fs.existsSync(configFileName)) {
58
- const content = await fs.promises.readFile(configFileName);
59
- config = JSON.parse(content.toString());
60
- }
61
- // eslint-disable-next-line no-underscore-dangle
62
- delete config._esmodule;
63
- return config;
64
- }
65
22
 
66
- function getConfigJson(vcm, name, { auth, config: configFile }) {
67
- if (configJson) {
68
- return Promise.resolve(configJson);
23
+ /**
24
+ * @param {ServeOptions} options
25
+ * @returns {Promise<void>}
26
+ */
27
+ export default async function serve(options) {
28
+ if (!fs.existsSync(path.join(getContext(), 'node_modules', '@vcmap', 'ui'))) {
29
+ logger.error('Can only serve in dev mode, if the map ui is a dependency of the current context');
30
+ return;
69
31
  }
70
- const isWebVcm = /^https?:\/\//.test(vcm);
71
- return new Promise((resolve, reject) => {
72
- function handleStream(stream) {
73
- let data = '';
74
- stream.on('data', (chunk) => {
75
- data += chunk.toString();
76
- });
32
+ await printVcmapUiVersion();
33
+ checkReservedDirectories();
34
+ const app = express();
35
+ logger.info('Starting development server...');
77
36
 
78
- stream.on('close', async () => {
79
- try {
80
- configJson = JSON.parse(data);
81
- configJson.plugins = configJson.plugins || [];
82
- const pluginConfig = await readConfigJson(configFile);
83
- // eslint-disable-next-line no-underscore-dangle
84
- pluginConfig.entry = '/_dist/plugin.js';
85
- pluginConfig.name = name;
86
- const idx = configJson.plugins.findIndex(p => p.name === name);
87
- if (idx > -1) {
88
- configJson.plugins.splice(idx, 1, pluginConfig);
89
- } else {
90
- configJson.plugins.push(pluginConfig);
91
- }
92
- resolve(configJson);
93
- } catch (e) {
94
- reject(e);
95
- }
96
- });
97
- }
98
- if (isWebVcm) {
99
- httpGet(`${vcm}config.json`, auth, (res) => {
100
- if (res.statusCode < 400) {
101
- handleStream(res);
102
- }
103
- });
104
- } else {
105
- handleStream(fs.createReadStream(path.join(vcm, 'config.json')));
106
- }
107
- });
108
- }
109
-
110
- async function serve(options) {
111
- logger.spin('Starting development server...');
112
- // eslint-disable-next-line prefer-const
113
- let { vcm, index } = options;
114
- const pluginName = options.pluginName || await getPluginName();
115
- const isWebVcm = /^https?:\/\//.test(vcm);
116
-
117
- const proxy = {};
118
- const indexFilename = 'index.html'; // XXX maybe use some random filename when web to not clobber anything
119
- if (isWebVcm) {
120
- vcm = `${vcm.replace(/\/$/, '')}/`;
121
- await getIndexHtml(`${vcm}/${index}`, indexFilename, options.auth);
122
- ['/lib', '/css', '/fonts', '/images', '/img', '/templates', '/datasource-data', '/plugins']
123
- .concat(options.proxyRoute) // TODO allow for more complex proxy options, e.g add a target such as --proxyRoute myProxy=myTarget
124
- .forEach((p) => {
125
- proxy[p] = {
126
- target: vcm,
127
- changeOrigin: true,
128
- auth: options.auth,
129
- followRedirects: true,
130
- };
131
- });
132
- }
133
- const webpackConfig = await getDevWebpackConfig(options);
134
- const server = new WebpackDevServer(webpack(webpackConfig), {
135
- hot: true,
136
- hotOnly: true,
137
- open: false,
138
- injectClient: false,
139
- publicPath: '/_dist',
140
- logLevel: 'warn',
141
- clientLogLevel: 'silent',
142
- useLocalIp: true,
143
- historyApiFallback: {
144
- disableDotRule: true,
145
- rewrites: [
146
- { from: /./, to: '/index.html' },
37
+ const server = await createServer({
38
+ root: getContext(),
39
+ publicDir: false,
40
+ optimizeDeps: {
41
+ exclude: [
42
+ '@vcmap/ui',
43
+ '@vcmap/core',
44
+ 'ol',
45
+ '@vcsuite/ui-components',
46
+ 'proj4',
47
+ ],
48
+ include: [
49
+ 'fast-deep-equal',
50
+ 'rbush-knn',
51
+ 'pbf',
52
+ '@vcmap/cesium',
147
53
  ],
148
54
  },
149
- staticOptions: {
150
- fallthrough: false,
151
- },
152
- before(app) {
153
- app.use('/config.json', (req, res) => {
154
- getConfigJson(vcm, pluginName, options)
155
- .then((config) => {
156
- const stringConfig = JSON.stringify(config, null, 2);
157
- res.setHeader('Content-Type', 'application/json');
158
- res.write(stringConfig);
159
- res.end();
160
- });
161
- });
162
- },
163
- after(app) {
164
- app.use('/', (err, req, res, next) => {
165
- if (err.statusCode === 404 && isWebVcm) {
166
- httpGet(`${vcm}${req.url.replace(/^\//, '')}`, options.auth, (innerRes) => {
167
- if (innerRes.statusCode < 400) {
168
- innerRes.pipe(res);
169
- }
170
- });
171
- } else {
172
- next();
173
- }
174
- });
55
+ plugins: [
56
+ createVuePlugin(),
57
+ createConfigJsonReloadPlugin(),
58
+ Components({
59
+ resolvers: [
60
+ VuetifyResolver(),
61
+ ],
62
+ include: [],
63
+ exclude: [],
64
+ }),
65
+ ],
66
+ server: {
67
+ middlewareMode: 'html',
68
+ https: options.https,
175
69
  },
176
- proxy,
177
- contentBase: isWebVcm ? getContext() : [options.vcm, getContext()],
178
- contentBasePublicPath: '/',
179
- index: 'index.html',
180
- });
181
-
182
- logger.stopSpinner();
183
- server.listen(options.port, '0.0.0.0', (err) => {
184
- if (err) {
185
- logger.error(err);
186
- } else {
187
- logger.success('Your application is running');
188
- }
189
70
  });
190
71
 
191
- ['SIGINT', 'SIGTERM'].forEach((signal) => {
192
- process.on(signal, () => {
193
- server.close(() => {
194
- if (isWebVcm) {
195
- fs.unlinkSync(resolveContext(index));
196
- }
197
- process.exit(0);
198
- });
199
- });
200
- });
201
- }
72
+ addMapConfigRoute(app, options.mapConfig, options.auth, options.config);
73
+ addIndexRoute(app, server);
202
74
 
75
+ app.use(server.middlewares);
203
76
 
204
- module.exports = serve;
77
+ const port = options.port || 8008;
78
+ await app.listen(port);
79
+ logger.info(`Server running on port ${port}`);
80
+ }
@@ -1,290 +0,0 @@
1
- const path = require('path');
2
- const webpack = require('webpack');
3
- const VueLoaderPlugin = require('vue-loader/lib/plugin');
4
- const TerserPlugin = require('terser-webpack-plugin');
5
- const autoprefixer = require('autoprefixer');
6
- const { getPluginEntry } = require('./packageJsonHelpers');
7
- const { resolveContext, getContext } = require('./context');
8
-
9
- /**
10
- * @enum {string}
11
- */
12
- const buildMode = {
13
- DEVELOPMENT: 'development',
14
- PRODUCTION: 'production',
15
- TESTING: 'testing',
16
- };
17
-
18
- /**
19
- * @typedef {GetWebpackOptions} DevOptions
20
- * @property {string|undefined} vcm - the vcm directory
21
- * @property {number} port - the port number of the dev server // XXX take these options apart
22
- */
23
-
24
- /**
25
- * @typedef {GetWebpackOptions} ProdOptions
26
- * @property {string} pluginName - the name of the plugin being built
27
- * @property {boolean|undefined} modern - build for modern browsers
28
- */
29
-
30
- /**
31
- * @typedef {Object} GetWebpackOptions
32
- * @property {string|Object|undefined} entry - an alternative entry point, defaults to 'src/index'
33
- * @property {string|undefined} mode - 'development', 'production' or 'test'.
34
- * @property {boolean|undefined} condenseWhitespace - pass whitespace: 'condense' to vue loader
35
- */
36
-
37
- /**
38
- * @param {GetWebpackOptions} options
39
- * @returns {webpack.Configuration}
40
- */
41
- function getBaseConfig(options) {
42
- return {
43
- experiments: {
44
- outputModule: true,
45
- },
46
- externals: {
47
- '@vcmap/core': 'import @vcmap-core.js',
48
- },
49
- entry: options.entry,
50
- context: getContext(),
51
- resolve: {
52
- extensions: ['.js', '.vue', '.json'],
53
- alias: {
54
- '@': resolveContext('src'),
55
- },
56
- modules: [
57
- path.join(__dirname, '..', 'node_modules'),
58
- 'node_modules',
59
- resolveContext('node_modules'),
60
- ],
61
- },
62
- resolveLoader: {
63
- modules: [
64
- path.join(__dirname, '..', 'node_modules'),
65
- 'node_modules',
66
- resolveContext('node_modules'),
67
- ],
68
- },
69
- module: {
70
- rules: [
71
- {
72
- test: /\.vue$/,
73
- loader: 'vue-loader',
74
- options: {
75
- compilerOptions: {
76
- whitespace: options.condenseWhitespace ? 'condense' : 'preserve',
77
- },
78
- },
79
- },
80
- {
81
- test: /\.js$/,
82
- loader: 'babel-loader',
83
- include: [resolveContext('src')],
84
- options: {
85
- presets: [
86
- require.resolve('@vue/babel-preset-app'),
87
- ],
88
- },
89
- },
90
- {
91
- test: /\.(png|jpe?g|gif)(\?.*)?$/,
92
- use: [
93
- {
94
- loader: 'url-loader',
95
- options: {
96
- limit: 10000,
97
- name: 'img/[name].[hash:8].[ext]',
98
- },
99
- },
100
- ],
101
- },
102
- {
103
- test: /\.(svg)(\?.*)?$/,
104
- use: [
105
- {
106
- loader: 'file-loader',
107
- options: {
108
- name: 'img/[name].[hash:8].[ext]',
109
- },
110
- },
111
- ],
112
- },
113
- {
114
- test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
115
- use: [
116
- {
117
- loader: 'url-loader',
118
- options: {
119
- limit: 10000,
120
- name: 'media/[name].[hash:8].[ext]',
121
- },
122
- },
123
- ],
124
- },
125
- {
126
- test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
127
- use: [
128
- {
129
- loader: 'url-loader',
130
- options: {
131
- limit: 10000,
132
- name: 'fonts/[name].[hash:8].[ext]',
133
- },
134
- },
135
- ],
136
- },
137
- {
138
- test: /\.css$/,
139
- use: [
140
- {
141
- loader: 'vue-style-loader',
142
- options: {
143
- base: 1020,
144
- shadowMode: false,
145
- },
146
- },
147
- {
148
- loader: 'css-loader',
149
- options: {
150
- sourceMap: false,
151
- importLoaders: 2,
152
- },
153
- },
154
- {
155
- loader: 'postcss-loader',
156
- options: {
157
- sourceMap: false,
158
- plugins: [
159
- autoprefixer,
160
- ],
161
- },
162
- },
163
- ],
164
- },
165
- {
166
- test: /\.p(ost)?css$/,
167
- use: [
168
- {
169
- loader: 'vue-style-loader',
170
- options: {
171
- base: 1020,
172
- shadowMode: false,
173
- },
174
- },
175
- {
176
- loader: 'css-loader',
177
- options: {
178
- sourceMap: false,
179
- importLoaders: 2,
180
- },
181
- },
182
- {
183
- loader: 'postcss-loader',
184
- options: {
185
- sourceMap: false,
186
- plugins: [
187
- autoprefixer,
188
- ],
189
- },
190
- },
191
- ],
192
- },
193
- ],
194
- },
195
- plugins: [
196
- new VueLoaderPlugin(),
197
- new webpack.DefinePlugin({
198
- 'process.env': { NODE_ENV: `"${options.mode}"`, BASE_URL: '""' },
199
- }),
200
- ],
201
- };
202
- }
203
-
204
- /**
205
- * @param {ProdOptions} options
206
- * @returns {Promise<webpack.Configuration>}
207
- */
208
- async function getProdWebpackConfig(options) {
209
- options.entry = options.entry || { plugin: await getPluginEntry() || './src/index' };
210
- options.mode = options.mode || buildMode.PRODUCTION;
211
- process.env.VUE_CLI_MODERN_BUILD = true;
212
-
213
- if (typeof options.entry === 'string') {
214
- options.entry = { plugin: options.entry };
215
- }
216
-
217
- const config = getBaseConfig(options);
218
- config.output = {
219
- path: resolveContext('dist'),
220
- filename: `${options.pluginName}.js`,
221
- library: {
222
- type: 'module',
223
- },
224
- publicPath: './',
225
- };
226
-
227
- config.mode = options.mode;
228
- if (options.mode !== 'development') {
229
- config.devtool = false;
230
- config.optimization = {
231
- minimize: true,
232
- minimizer: [
233
- new TerserPlugin({
234
- extractComments: false,
235
- }),
236
- ],
237
- };
238
- }
239
-
240
- return config;
241
- }
242
-
243
- /**
244
- * @param {DevOptions} options
245
- * @returns {Promise<webpack.Configuration>}
246
- */
247
- async function getDevWebpackConfig(options) {
248
- options.entry = options.entry || {
249
- plugin: [
250
- `webpack-dev-server/client?http://0.0.0.0:${options.port}/`,
251
- 'webpack/hot/only-dev-server',
252
- await getPluginEntry() || './src/index.js',
253
- ],
254
- };
255
- options.mode = options.mode || buildMode.DEVELOPMENT;
256
- if (typeof options.entry === 'string') {
257
- options.entry = {
258
- plugin: [
259
- `webpack-dev-server/client?http://0.0.0.0:${options.port}/`,
260
- 'webpack/hot/only-dev-server',
261
- options.entry,
262
- ],
263
- };
264
- }
265
- const config = getBaseConfig(options);
266
-
267
- config.output = {
268
- globalObject: '(typeof self !== \'undefined\' ? self : this)',
269
- path: resolveContext('_dist'),
270
- library: {
271
- type: 'module',
272
- },
273
- filename: '[name].js',
274
- publicPath: '/_dist',
275
- };
276
-
277
- config.plugins.push(
278
- new webpack.HotModuleReplacementPlugin(),
279
- // new webpack.NoEmitOnErrorsPlugin(),
280
- );
281
- config.mode = options.mode;
282
- config.devtool = 'eval-cheap-module-source-map';
283
- return config;
284
- }
285
-
286
- module.exports = {
287
- getBaseConfig,
288
- getProdWebpackConfig,
289
- getDevWebpackConfig,
290
- };
@@ -1,29 +0,0 @@
1
- const { getBaseConfig } = require('./getWebpackConfig');
2
- const { getPluginEntry } = require('./packageJsonHelpers');
3
-
4
- /**
5
- * Use this file to point your ID eslint settings to a webpack resolver. Do to a bug in `eslint-import-resolver-webpack`
6
- * you must provide the ENTRY env for use with eslint, see example.
7
- * @example
8
- * 'import/resolver': {
9
- * webpack: {
10
- * config: 'node_modules/vcmplugin-cli/src/webpack.config.js'
11
- * env: {
12
- * ENTRY: './index.js'
13
- * }
14
- * }
15
- * }
16
- * @param {Object=} env
17
- * @returns {webpack.Configuration|Promise<webpack.Configuration>}
18
- */
19
- function getConfig(env) {
20
- if (env && env.ENTRY) {
21
- return getBaseConfig({ entry: env.ENTRY, mode: 'development' });
22
- }
23
- return getPluginEntry()
24
- .then((entry) => {
25
- return getBaseConfig({ entry, mode: 'development' });
26
- });
27
- }
28
-
29
- module.exports = getConfig;