[Pkg-javascript-devel] Bug#927254: reproducible, but ...

Paolo Greppi paolo.greppi at libpf.com
Mon Jun 10 14:32:15 BST 2019


Hi, nice one !

It is reproducible when I use the laminar debs built from the WIP package here:
https://salsa.debian.org/debian/laminar
after these fixes:
https://bugs.debian.org/919181#27

Once started, the laminar service dashboard should be reachable from localhost:8080 but it fails to load.
If you open chromium inspector it reports:

vue-router.min.js:10 Uncaught ReferenceError: require is not defined
     at vue-router.min.js:10
(anonymous) @ vue-router.min.js:10
app.js:746 Uncaught ReferenceError: Chart is not defined
     at app.js:746
(anonymous) @ app.js:746
Navigated to http://10.0.3.188:8080/
vue.min.js:1 You are running Vue in development mode.
Make sure to turn on production mode when deploying for production.
See more tips at https://vuejs.org/guide/deployment.html

Interestingly, laminar package does not depend on libjs-vue-router because the file served from:
http://10.0.3.188:8080/js/vue-router.min.js
gets hard-coded in the /usr/sbin/laminard executable in at build time.
Indeed libjs-vue-router is a mere build-dep for laminar package:
https://salsa.debian.org/debian/laminar/blob/master/debian/control#L15

Currently it is hardcoding vue-router.common.js as vue-router.min.js:
https://salsa.debian.org/debian/laminar/blob/master/debian/patches/0001-Patch-build-system-to-use-JS-libraries-from-Debian-p.patch#L29
which is wrong.

I fixed the JS lib locations like this:
diff --git a/debian/patches/0001-Patch-build-system-to-use-JS-libraries-from-Debian-p.patch b/debian/patches/0001-Patch-build-system-to-use-JS-libraries-from-Debian-p.patch
index 0d6ca8f..e6ffdfe 100644
--- a/debian/patches/0001-Patch-build-system-to-use-JS-libraries-from-Debian-p.patch
+++ b/debian/patches/0001-Patch-build-system-to-use-JS-libraries-from-Debian-p.patch
@@ -26,11 +26,11 @@ index cf73a1b..d18dc65 100644
  -        css/bootstrap.min.css EXPECTED_MD5 5d5357cb3704e1f43a1f5bfed2aebf42)
  +file(DOWNLOAD file:///usr/share/javascript/vue/vue.min.js
  +              js/vue.min.js)
-+file(DOWNLOAD file:///usr/share/javascript/vue-router/vue-router.common.js
++file(DOWNLOAD file:///usr/lib/nodejs/vue-router/dist/vue-router.min.js
  +              js/vue-router.min.js)
-+file(DOWNLOAD file://usr/share/javascript/ansi_up/ansi_up.min.js
++file(DOWNLOAD file:///usr/share/javascript/ansi_up/ansi_up.min.js
  +              js/ansi_up.js)
-+file(DOWNLOAD file://usr/share/javascript/chart.js/Chart.min.js
++file(DOWNLOAD file:///usr/share/javascript/chart.js/Chart.min.js
  +              js/Chart.min.js)
  +file(DOWNLOAD file://usr/share/javascript/bootstrap/css/bootstrap.min.css
  +              css/bootstrap.min.css)

(notice the three forward slashes after file:)

This gets rid of the Chart error, unfortunately for vue-router.min.js, for that I just get a different error in chrome inspector:

vue-router.min.js:791 Uncaught TypeError: Regexp is not a function
     at compileRouteRegex (vue-router.min.js:791)
     at addRouteRecord (vue-router.min.js:723)
     at vue-router.min.js:681
     at Array.forEach (<anonymous>)
     at createRouteMap (vue-router.min.js:680)
     at createMatcher (vue-router.min.js:864)
     at new VueRouter (vue-router.min.js:1942)
     at app.js:796
compileRouteRegex @ vue-router.min.js:791
addRouteRecord @ vue-router.min.js:723
(anonymous) @ vue-router.min.js:681
createRouteMap @ vue-router.min.js:680
createMatcher @ vue-router.min.js:864
VueRouter @ vue-router.min.js:1942
(anonymous) @ app.js:796

Indeed comparing this file:
https://unpkg.com/vue-router@3.0.2/dist/vue-router.js
with the one packaged by us, they're different.

I attach the diff:
diff vue-router.js_unpkg vue-router.min.js_debian > diff

Paolo
-------------- next part --------------
3c3
<   * (c) 2018 Evan You
---
>   * (c) 2019 Evan You
7,10c7,12
< 	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
< 	typeof define === 'function' && define.amd ? define(factory) :
< 	(global.VueRouter = factory());
< }(this, (function () { 'use strict';
---
> 	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('path-to-regexp')) :
> 	typeof define === 'function' && define.amd ? define(['path-to-regexp'], factory) :
> 	(global.VueRouter = factory(global.Regexp));
> }(this, (function (Regexp) { 'use strict';
> 
> Regexp = Regexp && Regexp.hasOwnProperty('default') ? Regexp['default'] : Regexp;
21c23
<   if ("development" !== 'production' && !condition) {
---
>   if ("production" !== 'production' && !condition) {
127c129
< }
---
> };
140,146c142
<       {
<         warn(
<           false,
<           "props in \"" + (route.path) + "\" is a " + (typeof config) + ", " +
<           "expecting an object, function or boolean."
<         );
<       }
---
>       
177c173
<     "development" !== 'production' && warn(false, e.message);
---
>     "production" !== 'production' && warn(false, e.message);
247a244
> 
487c484
< }
---
> };
648,1080d644
< var isarray = Array.isArray || function (arr) {
<   return Object.prototype.toString.call(arr) == '[object Array]';
< };
< 
< /**
<  * Expose `pathToRegexp`.
<  */
< var pathToRegexp_1 = pathToRegexp;
< var parse_1 = parse;
< var compile_1 = compile;
< var tokensToFunction_1 = tokensToFunction;
< var tokensToRegExp_1 = tokensToRegExp;
< 
< /**
<  * The main path matching regexp utility.
<  *
<  * @type {RegExp}
<  */
< var PATH_REGEXP = new RegExp([
<   // Match escaped characters that would otherwise appear in future matches.
<   // This allows the user to escape special characters that won't transform.
<   '(\\\\.)',
<   // Match Express-style parameters and un-named parameters with a prefix
<   // and optional suffixes. Matches appear as:
<   //
<   // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?", undefined]
<   // "/route(\\d+)"  => [undefined, undefined, undefined, "\d+", undefined, undefined]
<   // "/*"            => ["/", undefined, undefined, undefined, undefined, "*"]
<   '([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))'
< ].join('|'), 'g');
< 
< /**
<  * Parse a string for the raw tokens.
<  *
<  * @param  {string}  str
<  * @param  {Object=} options
<  * @return {!Array}
<  */
< function parse (str, options) {
<   var tokens = [];
<   var key = 0;
<   var index = 0;
<   var path = '';
<   var defaultDelimiter = options && options.delimiter || '/';
<   var res;
< 
<   while ((res = PATH_REGEXP.exec(str)) != null) {
<     var m = res[0];
<     var escaped = res[1];
<     var offset = res.index;
<     path += str.slice(index, offset);
<     index = offset + m.length;
< 
<     // Ignore already escaped sequences.
<     if (escaped) {
<       path += escaped[1];
<       continue
<     }
< 
<     var next = str[index];
<     var prefix = res[2];
<     var name = res[3];
<     var capture = res[4];
<     var group = res[5];
<     var modifier = res[6];
<     var asterisk = res[7];
< 
<     // Push the current path onto the tokens.
<     if (path) {
<       tokens.push(path);
<       path = '';
<     }
< 
<     var partial = prefix != null && next != null && next !== prefix;
<     var repeat = modifier === '+' || modifier === '*';
<     var optional = modifier === '?' || modifier === '*';
<     var delimiter = res[2] || defaultDelimiter;
<     var pattern = capture || group;
< 
<     tokens.push({
<       name: name || key++,
<       prefix: prefix || '',
<       delimiter: delimiter,
<       optional: optional,
<       repeat: repeat,
<       partial: partial,
<       asterisk: !!asterisk,
<       pattern: pattern ? escapeGroup(pattern) : (asterisk ? '.*' : '[^' + escapeString(delimiter) + ']+?')
<     });
<   }
< 
<   // Match any characters still remaining.
<   if (index < str.length) {
<     path += str.substr(index);
<   }
< 
<   // If the path exists, push it onto the end.
<   if (path) {
<     tokens.push(path);
<   }
< 
<   return tokens
< }
< 
< /**
<  * Compile a string to a template function for the path.
<  *
<  * @param  {string}             str
<  * @param  {Object=}            options
<  * @return {!function(Object=, Object=)}
<  */
< function compile (str, options) {
<   return tokensToFunction(parse(str, options))
< }
< 
< /**
<  * Prettier encoding of URI path segments.
<  *
<  * @param  {string}
<  * @return {string}
<  */
< function encodeURIComponentPretty (str) {
<   return encodeURI(str).replace(/[\/?#]/g, function (c) {
<     return '%' + c.charCodeAt(0).toString(16).toUpperCase()
<   })
< }
< 
< /**
<  * Encode the asterisk parameter. Similar to `pretty`, but allows slashes.
<  *
<  * @param  {string}
<  * @return {string}
<  */
< function encodeAsterisk (str) {
<   return encodeURI(str).replace(/[?#]/g, function (c) {
<     return '%' + c.charCodeAt(0).toString(16).toUpperCase()
<   })
< }
< 
< /**
<  * Expose a method for transforming tokens into the path function.
<  */
< function tokensToFunction (tokens) {
<   // Compile all the tokens into regexps.
<   var matches = new Array(tokens.length);
< 
<   // Compile all the patterns before compilation.
<   for (var i = 0; i < tokens.length; i++) {
<     if (typeof tokens[i] === 'object') {
<       matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$');
<     }
<   }
< 
<   return function (obj, opts) {
<     var path = '';
<     var data = obj || {};
<     var options = opts || {};
<     var encode = options.pretty ? encodeURIComponentPretty : encodeURIComponent;
< 
<     for (var i = 0; i < tokens.length; i++) {
<       var token = tokens[i];
< 
<       if (typeof token === 'string') {
<         path += token;
< 
<         continue
<       }
< 
<       var value = data[token.name];
<       var segment;
< 
<       if (value == null) {
<         if (token.optional) {
<           // Prepend partial segment prefixes.
<           if (token.partial) {
<             path += token.prefix;
<           }
< 
<           continue
<         } else {
<           throw new TypeError('Expected "' + token.name + '" to be defined')
<         }
<       }
< 
<       if (isarray(value)) {
<         if (!token.repeat) {
<           throw new TypeError('Expected "' + token.name + '" to not repeat, but received `' + JSON.stringify(value) + '`')
<         }
< 
<         if (value.length === 0) {
<           if (token.optional) {
<             continue
<           } else {
<             throw new TypeError('Expected "' + token.name + '" to not be empty')
<           }
<         }
< 
<         for (var j = 0; j < value.length; j++) {
<           segment = encode(value[j]);
< 
<           if (!matches[i].test(segment)) {
<             throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '", but received `' + JSON.stringify(segment) + '`')
<           }
< 
<           path += (j === 0 ? token.prefix : token.delimiter) + segment;
<         }
< 
<         continue
<       }
< 
<       segment = token.asterisk ? encodeAsterisk(value) : encode(value);
< 
<       if (!matches[i].test(segment)) {
<         throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"')
<       }
< 
<       path += token.prefix + segment;
<     }
< 
<     return path
<   }
< }
< 
< /**
<  * Escape a regular expression string.
<  *
<  * @param  {string} str
<  * @return {string}
<  */
< function escapeString (str) {
<   return str.replace(/([.+*?=^!:${}()[\]|\/\\])/g, '\\$1')
< }
< 
< /**
<  * Escape the capturing group by escaping special characters and meaning.
<  *
<  * @param  {string} group
<  * @return {string}
<  */
< function escapeGroup (group) {
<   return group.replace(/([=!:$\/()])/g, '\\$1')
< }
< 
< /**
<  * Attach the keys as a property of the regexp.
<  *
<  * @param  {!RegExp} re
<  * @param  {Array}   keys
<  * @return {!RegExp}
<  */
< function attachKeys (re, keys) {
<   re.keys = keys;
<   return re
< }
< 
< /**
<  * Get the flags for a regexp from the options.
<  *
<  * @param  {Object} options
<  * @return {string}
<  */
< function flags (options) {
<   return options.sensitive ? '' : 'i'
< }
< 
< /**
<  * Pull out keys from a regexp.
<  *
<  * @param  {!RegExp} path
<  * @param  {!Array}  keys
<  * @return {!RegExp}
<  */
< function regexpToRegexp (path, keys) {
<   // Use a negative lookahead to match only capturing groups.
<   var groups = path.source.match(/\((?!\?)/g);
< 
<   if (groups) {
<     for (var i = 0; i < groups.length; i++) {
<       keys.push({
<         name: i,
<         prefix: null,
<         delimiter: null,
<         optional: false,
<         repeat: false,
<         partial: false,
<         asterisk: false,
<         pattern: null
<       });
<     }
<   }
< 
<   return attachKeys(path, keys)
< }
< 
< /**
<  * Transform an array into a regexp.
<  *
<  * @param  {!Array}  path
<  * @param  {Array}   keys
<  * @param  {!Object} options
<  * @return {!RegExp}
<  */
< function arrayToRegexp (path, keys, options) {
<   var parts = [];
< 
<   for (var i = 0; i < path.length; i++) {
<     parts.push(pathToRegexp(path[i], keys, options).source);
<   }
< 
<   var regexp = new RegExp('(?:' + parts.join('|') + ')', flags(options));
< 
<   return attachKeys(regexp, keys)
< }
< 
< /**
<  * Create a path regexp from string input.
<  *
<  * @param  {string}  path
<  * @param  {!Array}  keys
<  * @param  {!Object} options
<  * @return {!RegExp}
<  */
< function stringToRegexp (path, keys, options) {
<   return tokensToRegExp(parse(path, options), keys, options)
< }
< 
< /**
<  * Expose a function for taking tokens and returning a RegExp.
<  *
<  * @param  {!Array}          tokens
<  * @param  {(Array|Object)=} keys
<  * @param  {Object=}         options
<  * @return {!RegExp}
<  */
< function tokensToRegExp (tokens, keys, options) {
<   if (!isarray(keys)) {
<     options = /** @type {!Object} */ (keys || options);
<     keys = [];
<   }
< 
<   options = options || {};
< 
<   var strict = options.strict;
<   var end = options.end !== false;
<   var route = '';
< 
<   // Iterate over the tokens and create our regexp string.
<   for (var i = 0; i < tokens.length; i++) {
<     var token = tokens[i];
< 
<     if (typeof token === 'string') {
<       route += escapeString(token);
<     } else {
<       var prefix = escapeString(token.prefix);
<       var capture = '(?:' + token.pattern + ')';
< 
<       keys.push(token);
< 
<       if (token.repeat) {
<         capture += '(?:' + prefix + capture + ')*';
<       }
< 
<       if (token.optional) {
<         if (!token.partial) {
<           capture = '(?:' + prefix + '(' + capture + '))?';
<         } else {
<           capture = prefix + '(' + capture + ')?';
<         }
<       } else {
<         capture = prefix + '(' + capture + ')';
<       }
< 
<       route += capture;
<     }
<   }
< 
<   var delimiter = escapeString(options.delimiter || '/');
<   var endsWithDelimiter = route.slice(-delimiter.length) === delimiter;
< 
<   // In non-strict mode we allow a slash at the end of match. If the path to
<   // match already ends with a slash, we remove it for consistency. The slash
<   // is valid at the end of a path match, not in the middle. This is important
<   // in non-ending mode, where "/test/" shouldn't match "/test//route".
<   if (!strict) {
<     route = (endsWithDelimiter ? route.slice(0, -delimiter.length) : route) + '(?:' + delimiter + '(?=$))?';
<   }
< 
<   if (end) {
<     route += '$';
<   } else {
<     // In non-ending mode, we need the capturing groups to match as much as
<     // possible by using a positive lookahead to the end or next path segment.
<     route += strict && endsWithDelimiter ? '' : '(?=' + delimiter + '|$)';
<   }
< 
<   return attachKeys(new RegExp('^' + route, flags(options)), keys)
< }
< 
< /**
<  * Normalize the given path string, returning a regular expression.
<  *
<  * An empty array can be passed in for the keys, which will hold the
<  * placeholder key descriptions. For example, using `/user/:id`, `keys` will
<  * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.
<  *
<  * @param  {(string|RegExp|Array)} path
<  * @param  {(Array|Object)=}       keys
<  * @param  {Object=}               options
<  * @return {!RegExp}
<  */
< function pathToRegexp (path, keys, options) {
<   if (!isarray(keys)) {
<     options = /** @type {!Object} */ (keys || options);
<     keys = [];
<   }
< 
<   options = options || {};
< 
<   if (path instanceof RegExp) {
<     return regexpToRegexp(path, /** @type {!Array} */ (keys))
<   }
< 
<   if (isarray(path)) {
<     return arrayToRegexp(/** @type {!Array} */ (path), /** @type {!Array} */ (keys), options)
<   }
< 
<   return stringToRegexp(/** @type {string} */ (path), /** @type {!Array} */ (keys), options)
< }
< pathToRegexp_1.parse = parse_1;
< pathToRegexp_1.compile = compile_1;
< pathToRegexp_1.tokensToFunction = tokensToFunction_1;
< pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
< 
1094c658
<       (regexpCompileCache[path] = pathToRegexp_1.compile(path));
---
>       (regexpCompileCache[path] = Regexp.compile(path));
1097,1099d660
<     {
<       warn(false, ("missing param for " + routeMsg + ": " + (e.message)));
<     }
1149,1157d709
<   {
<     assert(path != null, "\"path\" is required in a route configuration.");
<     assert(
<       typeof route.component !== 'string',
<       "route config \"component\" for path: " + (String(path || name)) + " cannot be a " +
<       "string id. Use an actual component instead."
<     );
<   }
< 
1191,1202d742
<     {
<       if (route.name && !route.redirect && route.children.some(function (child) { return /^\/?$/.test(child.path); })) {
<         warn(
<           false,
<           "Named Route '" + (route.name) + "' has a default child route. " +
<           "When navigating to this named route (:to=\"{name: '" + (route.name) + "'\"), " +
<           "the default child route will not be rendered. Remove the name from " +
<           "this route and use the name of the default child route for named " +
<           "links instead."
<         );
<       }
<     }
1240c780
<     } else if ("development" !== 'production' && !matchAs) {
---
>     } else if ("production" !== 'production' && !matchAs) {
1251,1258c791
<   var regex = pathToRegexp_1(path, [], pathToRegexpOptions);
<   {
<     var keys = Object.create(null);
<     regex.keys.forEach(function (key) {
<       warn(!keys[key.name], ("Duplicate param keys in route with path: \"" + path + "\""));
<       keys[key.name] = true;
<     });
<   }
---
>   var regex = Regexp(path, [], pathToRegexpOptions);
1270a804
> 
1294,1296c828
<     } else {
<       warn(false, "relative params navigation requires a current route.");
<     }
---
>     } else {}
1328d859
< 
1352,1354d882
<       {
<         warn(record, ("Route with name '" + name + "' does not exist"));
<       }
1404,1408d931
<       {
<         warn(
<           false, ("invalid redirect option: " + (JSON.stringify(redirect)))
<         );
<       }
1424,1427d946
<       var targetRecord = nameMap[name];
<       {
<         assert(targetRecord, ("redirect failed: named route \"" + name + "\" not found."));
<       }
1448,1450d966
<       {
<         warn(false, ("invalid redirect option: " + (JSON.stringify(redirect))));
<       }
1524a1041
> 
1554,1558d1070
<   {
<     assert(typeof behavior === 'function', "scrollBehavior must be a function");
<   }
< 
<   // wait until re-render finishes before scrolling
1571,1573c1083
<         {
<           assert(false, err.toString());
<         }
---
>         
1761c1271
<           "development" !== 'production' && warn(false, msg);
---
>           "production" !== 'production' && warn(false, msg);
2136a1647
> 
2224a1736
> 
2361a1874
> 
2420,2421d1932
< 
< 
2454,2456c1965
<       {
<         assert(false, ("invalid mode: " + mode));
<       }
---
>       
2477c1986
<   "development" !== 'production' && assert(
---
>   "production" !== 'production' && assert(
2626a2136
> //# sourceMappingURL=vue-router.min.js.map


More information about the Pkg-javascript-devel mailing list