@stencil/dev-server 0.0.19-0 → 5.0.0-alpha.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/dist/index.js DELETED
@@ -1,275 +0,0 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- Object.defineProperty(exports, "__esModule", { value: true });
11
- const path = require("path");
12
- const url = require("url");
13
- const tinylr = require("tiny-lr");
14
- const ecstatic = require("ecstatic");
15
- const opn = require("opn");
16
- const http = require("http");
17
- const https = require("https");
18
- const chokidar_1 = require("chokidar");
19
- const utils_1 = require("./utils");
20
- const middlewares_1 = require("./middlewares");
21
- const discover_1 = require("@ionic/discover");
22
- const RESERVED_STENCIL_PATH = '/__stencil-dev-server__';
23
- const optionInfo = {
24
- root: {
25
- default: 'www',
26
- type: String
27
- },
28
- verbose: {
29
- default: false,
30
- type: Boolean
31
- },
32
- html5mode: {
33
- default: true,
34
- type: Boolean
35
- },
36
- watchGlob: {
37
- default: '**/*',
38
- type: String
39
- },
40
- address: {
41
- default: '0.0.0.0',
42
- type: String
43
- },
44
- httpPort: {
45
- default: 3333,
46
- type: Number
47
- },
48
- liveReloadPort: {
49
- default: 35729,
50
- type: Number
51
- },
52
- additionalJsScripts: {
53
- default: '',
54
- type: String
55
- },
56
- config: {
57
- default: './stencil.config.js',
58
- type: String
59
- },
60
- ssl: {
61
- default: false,
62
- type: Boolean
63
- }
64
- };
65
- function run(argv) {
66
- return __awaiter(this, void 0, void 0, function* () {
67
- const cliDefaultedOptions = utils_1.parseOptions(optionInfo, argv);
68
- cliDefaultedOptions.additionalJsScripts = cliDefaultedOptions.additionalJsScripts
69
- .split(',')
70
- .filter((name) => !!name);
71
- const isVerbose = cliDefaultedOptions.verbose;
72
- const configOptions = yield utils_1.parseConfigFile(process.cwd(), cliDefaultedOptions.config);
73
- const options = Object.keys(cliDefaultedOptions).reduce((options, optionName) => {
74
- const newValue = (configOptions[optionName] == null) ?
75
- cliDefaultedOptions[optionName] :
76
- configOptions[optionName];
77
- options[optionName] = newValue;
78
- return options;
79
- }, {});
80
- const [foundHttpPort, foundLiveReloadPort] = yield Promise.all([
81
- utils_1.findClosestOpenPort(options.address, options.httpPort),
82
- utils_1.findClosestOpenPort(options.address, options.liveReloadPort),
83
- ]);
84
- const protocol = options.ssl ? 'https' : 'http';
85
- log(isVerbose, `Will serve requests using : ${protocol}`);
86
- const wwwRoot = path.resolve(options.root);
87
- const browserUrl = getAddressForBrowser(options.address);
88
- const [tinyLrServer, lrScriptLocation, emitLiveReloadUpdate] = yield createLiveReload(foundLiveReloadPort, options.address, wwwRoot, options.ssl);
89
- const jsScriptLocations = options.additionalJsScripts
90
- .map((filePath) => filePath.trim())
91
- .concat(lrScriptLocation);
92
- const fileWatcher = createFileWatcher(wwwRoot, options.watchGlob, emitLiveReloadUpdate, isVerbose);
93
- log(isVerbose, `watching: ${wwwRoot} ${options.watchGlob}`);
94
- const requestHandler = createHttpRequestHandler(wwwRoot, jsScriptLocations, options.html5mode);
95
- const httpServer = options.ssl ? https.createServer(yield utils_1.getSSL(), requestHandler).listen(foundHttpPort)
96
- : http.createServer(requestHandler).listen(foundHttpPort);
97
- log(isVerbose, `listening on ${protocol}://${browserUrl}:${foundHttpPort}`);
98
- log(isVerbose, `serving: ${wwwRoot}`);
99
- if (argv.indexOf('--no-open') === -1) {
100
- opn(`${protocol}://${browserUrl}:${foundHttpPort}`);
101
- }
102
- if (argv.indexOf('--broadcast') >= 0) {
103
- log(isVerbose, 'publishing broadcast');
104
- discover_1.newSilentPublisher('devapp', 'stencil-dev', foundHttpPort);
105
- }
106
- function close() {
107
- return __awaiter(this, void 0, void 0, function* () {
108
- fileWatcher.close();
109
- tinyLrServer.close();
110
- yield new Promise((resolve, reject) => {
111
- httpServer.close((err) => {
112
- if (err) {
113
- reject(err);
114
- }
115
- resolve();
116
- });
117
- });
118
- });
119
- }
120
- process.once('SIGINT', () => __awaiter(this, void 0, void 0, function* () {
121
- yield close();
122
- process.exit(0);
123
- }));
124
- return {
125
- httpServer,
126
- fileWatcher,
127
- tinyLrServer,
128
- close
129
- };
130
- });
131
- }
132
- exports.run = run;
133
- function createHttpRequestHandler(wwwDir, jsScriptsList, html5mode) {
134
- const jsScriptsMap = jsScriptsList.reduce((map, fileUrl) => {
135
- const urlParts = url.parse(fileUrl);
136
- if (urlParts.host) {
137
- map[fileUrl] = fileUrl;
138
- }
139
- else {
140
- const baseFileName = path.basename(fileUrl);
141
- map[path.join(RESERVED_STENCIL_PATH, 'js_includes', baseFileName)] = path.resolve(process.cwd(), fileUrl);
142
- }
143
- return map;
144
- }, {});
145
- const staticFileMiddleware = ecstatic({ root: wwwDir, cache: 0 });
146
- const devServerFileMiddleware = ecstatic({ root: path.resolve(__dirname, '..', 'assets') });
147
- const sendHtml = middlewares_1.serveHtml(wwwDir, Object.keys(jsScriptsMap));
148
- const sendDirectoryContents = middlewares_1.serveDirContents(wwwDir);
149
- return function (req, res) {
150
- return __awaiter(this, void 0, void 0, function* () {
151
- const reqPath = utils_1.getRequestedPath(req.url || '');
152
- const filePath = utils_1.getFileFromPath(wwwDir, req.url || '');
153
- let pathStat;
154
- const serveIndexFile = (directory) => __awaiter(this, void 0, void 0, function* () {
155
- const indexFilePath = path.join(directory, 'index.html');
156
- let indexFileStat;
157
- try {
158
- indexFileStat = yield utils_1.fsStatPr(indexFilePath);
159
- }
160
- catch (err) { }
161
- if (indexFileStat && indexFileStat.isFile()) {
162
- return sendHtml(indexFilePath, req, res);
163
- }
164
- });
165
- // If the file is a member of the scripts we autoload then serve it
166
- if (jsScriptsMap[(req.url || '')]) {
167
- return middlewares_1.sendFile('application/javascript', jsScriptsMap[(req.url || '')], req, res);
168
- }
169
- // If the request is to a static file that is part of this package
170
- // then just send it on using the static file middleware
171
- if ((req.url || '').startsWith(RESERVED_STENCIL_PATH)) {
172
- return devServerFileMiddleware(req, res);
173
- }
174
- try {
175
- pathStat = yield utils_1.fsStatPr(filePath);
176
- }
177
- catch (err) {
178
- // File or path does not exist
179
- if (err.code === 'ENOENT' || err.code === 'ENOTDIR') {
180
- if (html5mode && (['.html', ''].indexOf(path.extname(filePath)) !== -1)) {
181
- yield serveIndexFile(wwwDir);
182
- if (res.finished) {
183
- return;
184
- }
185
- }
186
- // The wwwDir index.html file does not exist.
187
- return middlewares_1.sendError(404, res, { error: err });
188
- }
189
- // No access to the file.
190
- if (err.code === 'EACCES') {
191
- return middlewares_1.sendError(403, res, { error: err });
192
- }
193
- // Not sure what happened.
194
- return middlewares_1.sendError(500, res, { error: err });
195
- }
196
- // If this is the first request then try to serve an index.html file in the root dir
197
- if (reqPath === '/') {
198
- yield serveIndexFile(wwwDir);
199
- if (res.finished) {
200
- return;
201
- }
202
- }
203
- // If the request is to a directory then serve the directory contents
204
- if (pathStat.isDirectory()) {
205
- yield serveIndexFile(filePath);
206
- if (res.finished) {
207
- return;
208
- }
209
- // If the request is to a directory but does not end in slash then redirect to use a slash
210
- if (!reqPath.endsWith('/')) {
211
- res.writeHead(302, {
212
- 'location': reqPath + '/'
213
- });
214
- return res.end();
215
- }
216
- return yield sendDirectoryContents(filePath, req, res);
217
- }
218
- // If the request is to a file and it is an html file then use sendHtml to parse and send on
219
- if (pathStat.isFile() && filePath.endsWith('.html')) {
220
- return yield sendHtml(filePath, req, res);
221
- }
222
- if (pathStat.isFile()) {
223
- return staticFileMiddleware(req, res);
224
- }
225
- // Not sure what you are requesting but lets just send an error
226
- return middlewares_1.sendError(415, res, { error: 'Resource requested cannot be served.' });
227
- });
228
- };
229
- }
230
- let timeoutId;
231
- function createFileWatcher(wwwDir, watchGlob, changeCb, isVerbose) {
232
- const watcher = chokidar_1.watch(watchGlob, {
233
- cwd: wwwDir,
234
- ignored: /(^|[\/\\])\../ // Ignore dot files, ie .git
235
- });
236
- function fileChanged(filePath) {
237
- clearTimeout(timeoutId);
238
- timeoutId = setTimeout(() => {
239
- log(isVerbose, `[${new Date().toTimeString().slice(0, 8)}] ${filePath} changed`);
240
- changeCb([filePath]);
241
- }, 50);
242
- }
243
- watcher.on('change', fileChanged);
244
- watcher.on('error', (err) => {
245
- log(true, err.toString());
246
- });
247
- return watcher;
248
- }
249
- function createLiveReload(port, address, wwwDir, ssl) {
250
- return __awaiter(this, void 0, void 0, function* () {
251
- const options = ssl ? yield utils_1.getSSL() : {};
252
- const protocol = ssl ? 'https' : 'http';
253
- const liveReloadServer = tinylr(options);
254
- liveReloadServer.listen(port, address);
255
- return [
256
- liveReloadServer,
257
- `${protocol}://${getAddressForBrowser(address)}:${port}/livereload.js?snipver=1`,
258
- (changedFiles) => {
259
- liveReloadServer.changed({
260
- body: {
261
- files: changedFiles.map(changedFile => ('/' + path.relative(wwwDir, changedFile)))
262
- }
263
- });
264
- }
265
- ];
266
- });
267
- }
268
- function getAddressForBrowser(ipAddress) {
269
- return (ipAddress === '0.0.0.0') ? 'localhost' : ipAddress;
270
- }
271
- function log(test, ...args) {
272
- if (test) {
273
- console.log(...args);
274
- }
275
- }
@@ -1,106 +0,0 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- Object.defineProperty(exports, "__esModule", { value: true });
11
- const path = require("path");
12
- const url = require("url");
13
- const fs = require("fs");
14
- const utils_1 = require("./utils");
15
- function serveHtml(wwwDir, scriptLocations) {
16
- return function (filePath, req, res) {
17
- return __awaiter(this, void 0, void 0, function* () {
18
- const indexHtml = yield utils_1.fsReadFilePr(filePath);
19
- const appendString = scriptLocations.map(sl => `<script type="text/javascript" src="${sl}" charset="utf-8"></script>`).join('\n');
20
- const htmlString = indexHtml.toString()
21
- .replace(`</body>`, `${appendString}
22
- </body>`);
23
- res.writeHead(200, {
24
- 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0',
25
- 'Expires': '0',
26
- 'Content-Type': 'text/html'
27
- });
28
- res.end(htmlString);
29
- });
30
- };
31
- }
32
- exports.serveHtml = serveHtml;
33
- function serveDirContents(wwwDir) {
34
- return function (dirPath, req, res) {
35
- return __awaiter(this, void 0, void 0, function* () {
36
- let files;
37
- const dirUrl = req.url;
38
- if (!dirUrl) {
39
- return sendError(500, res, { err: 'Somthing is not right' });
40
- }
41
- try {
42
- files = yield utils_1.fsReadDirPr(dirPath);
43
- }
44
- catch (err) {
45
- return sendError(500, res, { err: err });
46
- }
47
- const templateSrc = yield utils_1.fsReadFilePr(path.join(__dirname, '..', 'assets', 'index.html'));
48
- if (!templateSrc) {
49
- throw new Error('wait, where is my template src.');
50
- }
51
- files = files
52
- .filter((fileName) => '.' !== fileName[0]) // remove hidden files
53
- .sort();
54
- const fileStats = yield Promise.all(files.map((fileName) => utils_1.fsStatPr(path.join(dirPath, fileName))));
55
- if (dirUrl !== '/') {
56
- const dirStat = yield utils_1.fsStatPr(dirPath);
57
- files.unshift('..');
58
- fileStats.unshift(dirStat);
59
- }
60
- const fileHtml = files
61
- .map((fileName, index) => {
62
- const isDirectory = fileStats[index].isDirectory();
63
- return (`<span class="denote">${isDirectory ? 'd' : '-'}</span> <a class="${isDirectory ? 'directory' : 'file'}" href="${url.resolve(dirUrl, fileName)}">${fileName}</a>`);
64
- })
65
- .join('<br/>\n');
66
- const templateHtml = templateSrc.toString()
67
- .replace('{directory}', dirPath)
68
- .replace('{files}', fileHtml)
69
- .replace('{linked-path}', dirUrl.replace(/\//g, ' / '));
70
- res.writeHead(200, {
71
- 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0',
72
- 'Expires': '0',
73
- 'Content-Type': 'text/html'
74
- });
75
- res.end(templateHtml);
76
- });
77
- };
78
- }
79
- exports.serveDirContents = serveDirContents;
80
- function sendFile(contentType, filePath, req, res) {
81
- return __awaiter(this, void 0, void 0, function* () {
82
- const stat = yield utils_1.fsStatPr(filePath);
83
- if (!stat.isFile()) {
84
- return sendError(404, res, { error: 'File not found' });
85
- }
86
- res.writeHead(200, {
87
- 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0',
88
- 'Expires': '0',
89
- 'Content-Type': contentType,
90
- 'Content-Length': stat.size
91
- });
92
- fs.createReadStream(filePath)
93
- .pipe(res);
94
- });
95
- }
96
- exports.sendFile = sendFile;
97
- function sendError(httpStatus, res, content = {}) {
98
- res.writeHead(httpStatus, {
99
- 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0',
100
- 'Expires': '0',
101
- 'Content-Type': 'text/plain'
102
- });
103
- res.write(JSON.stringify(content, null, 2));
104
- res.end();
105
- }
106
- exports.sendError = sendError;
package/dist/promisify.js DELETED
@@ -1,18 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- /**
4
- * @example: const rReadFile = promisify<Buffer, string>(fs.readFile);
5
- *
6
- */
7
- exports.promisify = function (func) {
8
- return (...args) => {
9
- return new Promise((resolve, reject) => {
10
- func(...args, (err, response) => {
11
- if (err) {
12
- return reject(err);
13
- }
14
- resolve(response);
15
- });
16
- });
17
- };
18
- };
package/dist/utils.js DELETED
@@ -1,139 +0,0 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- Object.defineProperty(exports, "__esModule", { value: true });
11
- const path = require("path");
12
- const url = require("url");
13
- const fs = require("fs");
14
- const promisify_1 = require("./promisify");
15
- const devcert_san_1 = require("devcert-san");
16
- exports.fsStatPr = promisify_1.promisify(fs.stat);
17
- exports.fsReadFilePr = promisify_1.promisify(fs.readFile);
18
- exports.fsReadDirPr = promisify_1.promisify(fs.readdir);
19
- function findClosestOpenPort(host, port) {
20
- return __awaiter(this, void 0, void 0, function* () {
21
- function t(portToCheck) {
22
- return __awaiter(this, void 0, void 0, function* () {
23
- const isTaken = yield isPortTaken(host, portToCheck);
24
- if (!isTaken) {
25
- return portToCheck;
26
- }
27
- return t(portToCheck + 1);
28
- });
29
- }
30
- return t(port);
31
- });
32
- }
33
- exports.findClosestOpenPort = findClosestOpenPort;
34
- function isPortTaken(host, port) {
35
- return new Promise((resolve, reject) => {
36
- var net = require('net');
37
- var tester = net.createServer()
38
- .once('error', function (err) {
39
- if (err.code !== 'EADDRINUSE') {
40
- return resolve(true);
41
- }
42
- resolve(true);
43
- })
44
- .once('listening', function () {
45
- tester.once('close', function () {
46
- resolve(false);
47
- })
48
- .close();
49
- })
50
- .on('error', (err) => {
51
- reject(err);
52
- })
53
- .listen(port, host);
54
- });
55
- }
56
- exports.isPortTaken = isPortTaken;
57
- function parseOptions(optionInfo, argv) {
58
- return Object.keys(optionInfo).reduce((options, key) => {
59
- let foundIndex = argv.indexOf(`--${key}`);
60
- if (foundIndex === -1) {
61
- options[key] = optionInfo[key].default;
62
- return options;
63
- }
64
- switch (optionInfo[key].type) {
65
- case Boolean:
66
- options[key] = true;
67
- break;
68
- case Number:
69
- options[key] = parseInt(argv[foundIndex + 1], 10);
70
- break;
71
- default:
72
- options[key] = argv[foundIndex + 1];
73
- }
74
- return options;
75
- }, {});
76
- }
77
- exports.parseOptions = parseOptions;
78
- function parseConfigFile(baseDir, filePath) {
79
- return __awaiter(this, void 0, void 0, function* () {
80
- let config = {};
81
- try {
82
- const configFile = yield Promise.resolve().then(function () { return require(path.resolve(baseDir, filePath)); });
83
- config = configFile.devServer || {};
84
- }
85
- catch (err) {
86
- if (err.code === 'ENOENT' || err.code === 'ENOTDIR') {
87
- throw new Error(`The specified configFile does not exist: ${filePath}`);
88
- }
89
- if (err.code === 'EACCES') {
90
- throw new Error(`You do not have permission to read the specified configFile: ${filePath}`);
91
- }
92
- }
93
- return config;
94
- });
95
- }
96
- exports.parseConfigFile = parseConfigFile;
97
- function getRequestedPath(requestUrl) {
98
- const parsed = url.parse(requestUrl);
99
- decodeURIComponent(requestUrl);
100
- return decodePathname(parsed.pathname || '');
101
- }
102
- exports.getRequestedPath = getRequestedPath;
103
- function getFileFromPath(wwwRoot, requestUrl) {
104
- const pathname = getRequestedPath(requestUrl);
105
- return path.normalize(path.join(wwwRoot, path.relative('/', pathname)));
106
- }
107
- exports.getFileFromPath = getFileFromPath;
108
- function getSSL() {
109
- return __awaiter(this, void 0, void 0, function* () {
110
- return installSSL().then((cert) => {
111
- return {
112
- key: fs.readFileSync(cert.keyPath, 'utf-8'),
113
- cert: fs.readFileSync(cert.certPath, 'utf-8')
114
- };
115
- });
116
- });
117
- }
118
- exports.getSSL = getSSL;
119
- function installSSL() {
120
- try {
121
- // Certificates are cached by name, so two calls for getDevelopmentCertificate('foo') will return the same key and certificate
122
- return devcert_san_1.default('stencil-dev-server-ssl', {
123
- installCertutil: true
124
- });
125
- }
126
- catch (err) {
127
- throw new Error(`Failed to generate dev SSL certificate: ${err}\n`);
128
- }
129
- }
130
- function decodePathname(pathname) {
131
- const pieces = pathname.replace(/\\/g, "/").split('/');
132
- return pieces.map((piece) => {
133
- piece = decodeURIComponent(piece);
134
- if (process.platform === 'win32' && /\\/.test(piece)) {
135
- throw new Error('Invalid forward slash character');
136
- }
137
- return piece;
138
- }).join('/');
139
- }