I'm building a REST API that will live in AWS Lambda. I'm using apex to deploy it, and since I'm implementing it in javascript, I'm using webpack and babel to get my ES6+ goodies. I've discovered a problem with respect to #providesModule names, and I haven't been able to work around it.
I just created a new lambda function, and when I compile it, I can see from the output that one module couldn't be found -- but the other one can. Here's a sketch of the filesystem:
functions
├─ database.js #providesModule Database -- this works
├─ configuration.js #providesModule MyConfig -- this does not work
├── entity-post
├── index.js imports both 'Database' & 'MyConfig'
When I compile the lambda, the output indicates that the MyConfig dependency couldn't be resolved, but that Database could be. I've tried replacing the content of configuration.js with dead-simple code, in case resolution is failing on a basic parse error; no luck. I should add that I've got 5 other lambda functions in the same project that use similar imports, and they all work great.
I assume this is some kind of caching problem: the module resolution system hasn't noticed the #providesModule inside configuration.js.
How do I clear that cache? For that matter, which piece of software is even handling these resolutions? I've tried looking around online, but it's not clear whether recognition of #providesModule is a webpack thing, or a babel thing, or a webpack-babel-loader thing, or maybe just a node v5 thing.
I've done a lot of digging, both online and within my project's codebase. Here's what I've learned:
Almost all of the links you'll see online about #providesModule are about react or react-native projects. That's because Facebook's tooling -- specifically HasteMap (I think), part of the react-native packager suite -- explicitly adds support for #providesModule. My project is neither react nor react-native, and it doesn't use any Facebook tools in the build process. My project uses webpack v1.14 and babel v6.22 to build the source.
I've found the error message within webpack's source, at ./node_modules/webpack/lib/dependencies/WebpackMissingModule.js:
exports.moduleCode = function(request) {
return "var e = new Error(" + JSON.stringify("Cannot find module \"" + request + "\"") + "); " +
"e.code = 'MODULE_NOT_FOUND'; " +
"throw e;";
};
Webpack seems to have a term for scripts that declare a module name in this way: 'Labeled Modules'. There are several scripts within webpack's source that deal with labeled modules, but I haven't been able to pin down the code that recognizes a label within a module. My assumption is that the problem exists in the part of the system that populates the module registry, and the labeled module code I've found all seems to be related to accessing those modules within the (already populated) registry.
It's hard to trace this much further within webpack's source code, which is understandably very meta, and distributed across a bajillion files.
Any help troubleshooting this, or links to relevant documentation or framework code that shows how #providesModule is handled, is appreciated. I want to understand why my new module isn't resolving, so I can fix that problem. If my caching theory is off-base, fine.
This may not come as a surprise to most, but webpack doesn't (currently) honor #providesModule directives.
The reason it appeared as though it did is that webpack seems to flatten case when doing filename comparisons, and my filenames generally match the labels that I give them:
database.js == #providesModule Database
assemble.js == #providesModule Assemble
The reason I ran into trouble is that configuration.js != #providesModule MyConfig.
This also means the question I asked originally was off-base, in that I assumed resolution was failing because labels were being cached, when in fact labels were being ignored. Facebook's HasteMap may indeed use a cache, but it wasn't the culprit here.
Related
I'm using webpack 4.26.1 (latest).
The code import('./images/header.csv') produce the following error:
Uncaught (in promise) Error: Cannot find module './images/header.csv'
at webpackMissingModule (home.js:9)
My project structure:
'project-dir/src/components/home.js' (im here)
'project-dir/src/components/images/header.csv'
I tried to read https://webpack.js.org/api/module-methods/ but failed to understand what to do except adding random webpack comments which I don't understand.
Also, from the docs, I may be found the source of the problem but I'm not exactly sure I understand it and how to solve it.
Fully dynamic statements, such as import(foo), will fail because webpack requires at least some file location information. This is because foo could potentially be any path to any file in your system or project. The import() must contain at least some information about where the module is located, so bundling can be limited to a specific directory or set of files.
Every module that could potentially be requested on an import() call is included. For example, import(./locale/${language}.json) will cause every .json file in the ./locale directory to be bundled into the new chunk. At run time, when the variable language has been computed, any file like english.json or german.json will be available for consumption. Using the webpackInclude and webpackExclude options allows us to add regex patterns that reduce the files that webpack will bundle for this import.
More than providing me a solution, I will appreciate any answer that covers what is the actual problem with my code.
Thank you.
I decided to try out WebPack on a new project I'm spinning up today and I'm getting really strange behavior from the sourcemaps. I can't find anything about it in the documentation, nor can I find anyone else having this issue when skimming StackOverflow.
I'm currently looking at the HelloWorld app produced by Vue-CLI's WebPack template -- no changes have been made to the code, the build environment, or anything.
I installed everything and ran it like so:
vue init webpack test && cd test && npm install && npm run dev
Looking at my sourcemaps, I see the following:
This is a hot mess. Why are there three version of HelloWorld.vue and App.vue? Worse yet, each version has a slightly different version of the code and none of them match the original source. The HellowWorld.vue sitting in the root directory does match the original source, but what's it doing down there instead of in the ./src/components folder? Finally, why isn't there a fourth App.vue that has the original source for it?
As far as I can tell this may have something to do with the WebPack loaders. I've never gotten these kinds of issues with any other bundler, though. Below is an example of the exact same steps using the Browserify Vue-CLI template:
No webpack:// schema, only one copy of every file, the files actually contain the original source code (kind of important for source maps), no unexpected (webpack)/buildin or (webpack)-hot-middleware, no . subdirectory,.... just the source code.
I haven't worked with Vue so can't really describe how exactly this is happening but it seems to be related to Vue Loader. Looking at the documentation I did not really find anything that clarifies why it would create three different files for one component. But it does seem logical considering that a .vue file might contain three types of top-level language blocks: <template>, <script>, and <style>.
Also, looking at two of those files you do see a comment at end of each file that suggests it was modified in some way by a Vue loader. Either this
//////////////////
// WEBPACK FOOTER
// ./node_modules/vue-loader/lib/template-compiler
or
//////////////////
// WEBPACK FOOTER
// ./node_modules/vue-style-loader!./node_modules/css-loader
The third file is different but it still does have code that identifies it as being modified by Vue loader. Here is some of that code
function injectStyle (ssrContext) {
if (disposed) return
require("!!vue-style-loader...")
}
/* script */
import __vue_script__ from "!!babel-loader!../../node_modules/vue-loader/..."
/* template */
import __vue_template__ from "!!../../node_modules/vue-loader/..."
/* styles */
var __vue_styles__ = injectStyle
The document also says this:
vue-loader is a loader for Webpack that can transform Vue components written in the following format into a plain JavaScript module:
Which explains why you might not see the same type of behaviour with other bundlers.
Now, This might not be the answer you were looking for but just wanted to share what I had found.
This is actually a feature of webpack.
webpack has HMR (Hot Module Reloading). If you look in your network tab, go ahead and make an update to your HelloWorld.vue file. You'll see a js chunk come thru as well as an updated JSON manifest. Both of these will have a unique hash at the end for each time you make a change to the application. It does this so the browser does not have to do a full reload.
For a better explanation of this I would highly recommend reading through https://webpack.js.org/concepts/hot-module-replacement/
I'm following the standard practice of organizing my angular assets by feature; e.g. AngularJS Folder Structure and AngularJS Best Practices: Directory Structure.
Which file should I put my module / dependency declaration in?
I'm trying to solve the following problems:
I'd like to be able to sort my <script> references alphabetically for maintenance reasons, but I can't because that breaks my Angular bootstrap (for some modules).
I've tried keeping them in the alphabetically-first *.js file in the module, but I spend a lot of time as my app grows moving my dependency declarations around.
I often have to hunt around to find module declarations.
I end up staring at Angular's relatively uninformative module error too often for related reasons.
Regardless, attaching the module declaration to a specific controller seems to imply a direct correlation that doesn't exist.
Here's an example:
metric/
_module.js // Should I create this file?
detail-controller.js
detail.html
search-filter.js
selector-controller.js
selector-directive.js
selector.html
Currently, for this module, that line of code exists in one of my module's controllers, you guess which one! ;)
As a possible solution that I'm not entirely happy with, should I put each module definition in its own tiny, one-line file?
angular.module('metric', ['lib', 'ngSanitize', 'ui.select', 'data']);
How do you do this? Am I missing some other clever or obvious solution?
p.s. as a related problem, if you feel like it, how do to track which components of your module are the source(s) of the dependency?
I would break it up even further.
metric/
metric.js
controllers/
detail-controller.js
selector-controller.js
directives/
selector-directive.js
filters/
search-filter.js
templates/
detail.html
selector.html
Now that I've been working with it for a while, and because I've started pre-compiling my javascript with gulp, the one-line module declaration file seems to be the best solution for me.
I name that file <special-character>module.js, so that it sorts visually and at compile-time to the top. Because my layout convention is one folder = one module, this works schematically. My special character is dash, YMMV. My individual .js file names don't show up in the production compiled version anyway.
It initially bothered me that there was a one-line file in my project, but now I appreciate it. It gets compiled in to my application javascript with gulp, so it's not a performance issue. Also, there's an obvious place to look for dependencies, clear trail in revision control logs of dependency changes, and simple process to document dependencies from my sources with my own custom tools.
Has any one had any success with this?
I think it's more or less an unsolved problem:
https://github.com/jashkenas/coffee-script/issues/2779 . Last meanigingful comment was from jwalton, a month ago.
Still, it doesn't seem rocket science to add support for it, so it will probably come soon.
Michael Ficarra (creator of CoffeeScript Redux) suggested using https://github.com/michaelficarra/commonjs-everywhere .
Two caveats:
It only works for bundling CommonJS modules.
It uses CoffeeScript Redux, which is still in beta (although working quite well it seems), and not 100% compatible with original CoffeeScript compiler.
So this does not work for what you ask for specifically, "concatenation".
Added April 14
You might have luck with these: combine-source-map and/or generate-sourcemap, both by same author.
Added April 26
This looks really simple: https://npmjs.org/package/mapcat . You just have to feed it the individual source map files generated by the coffee compiler.
Added May 16
Mariusz Nowak has just released webmake-coffee. Like CommonJS Everywhere, it requires code to be organized as CommonJS modules. Unlike CommonJS everywhere, it uses regular CoffeeScript.
It also seems the Grunt Coffee-Script plugin has had source-map support for concatenated files for quite a while (two months), effectively proving my original answer to be incorrect.
The upcoming version 2.0 of Snockets will have support for it too.
I ended up going with browserify using coffeeify as the transform option, and enabling browserify's debug option. I bundle up the app on each request for my main.js file, and any runtime errors show up in my original source with pretty decent accuracy.
Sure beats mapping runtime errors in the concatenated/compiled js back to the coffee source with my eyeballs!
I needed to annotate AngularJS code before minification, but grunt-ng-annotate didn't accept input source maps, thus I would not be able to use maps generated by the CoffeeScript compiler.
Apparently, with gulp-sourcemaps this is not an issue:
var gulp = require('gulp');
var $ = require('gulp-load-plugins')(); // loading gulp plugins lazily
// remember to include them in the package.json
gulp.task('appJS', function() {
// concatenate compiled .coffee files and js files into build/app.js
gulp.src(['./app/**/*.js','./app/**/*.coffee'])
.pipe($.sourcemaps.init())
.pipe($['if'](/[.]coffee$/, $.coffee({bare: true}).on('error', $.util.log)))
.pipe($.concat('app.js'))
.pipe($.ngAnnotate())
.pipe($.uglify())
.pipe($.sourcemaps.write())
.pipe(gulp.dest('./build'))
});
The same approach works in other situations, too. In my case, this is the only approach that worked.
I have written a grunt task that does this flawless. Check it out
My situation is as follows:
My project based on RequireJS.
I am using RequireJS Optimizer for to create a single JS file.
Some of the module use a certain third party library as a dependency.
The third party is NOT included in the optimized file (libName: empty
in the build config).
RequireJS is configured through var require = {} object which appears
on EACH PAGE, right above the RequireJS. The object defines a path to
the unminifed version of the library, among other things.
What i'd like to achieve:
Use the same config file in both development and production (the require={} object is included with tag on each page). During development I'd like modules to use the UNMINIFIED version of the third party.However, after optimization occurs, i would like all the modules to use the minified version of that third party.
I did think of a solution in theory, but it seems a bit messy and Im hopeful cleaner solution exists:
To have the runtime config point to unminified version
var require = {
paths:{
'thirdParty':'lib/thirdParty'
}
}
Create a module which execute (lets call it "PathRewrite" Module):
requirejs.config({
paths:{
'thirdParty':'lib/thirdParty.min'
}
})
In runtime configuration, define path to "PathRewrite" as empty
var require = {
paths:{
'thirdParty':'lib/thirdParty',
'PathRewrite':'empty'
}
}
In the build configuration file define a real Path to "PathRewrite" in order for it to be included in the "main" file (concatenated file after build).
Include "PathRewrite" as a dependency of a module which is executed first.
What I hope that will happen is that during dev, when optimized file is not used, PathRewrite is will not be used, hence the path to unminified third party in the runtime config will be used.
When the project is optimized, PathRewrite will be included and executed. According to RequireJS documentation, it is possible to run RequireJS configuration twice and the configuration will be appended/overwritten. PathRewrite execution will overwrite the path to "thirdParty" to minified, which will thus be used by all the modules.
Hopefully i've provided enough information. I'd be glad hear of other ways to get this done. Thanks in advance.
This topic appears to have been explored a bit in this answer:
Loading min.js files Generated by TypeScript with Require
Don't let the title discourage you. Typescript is not the core issue of the question being answered there. Unfortunately, the discussion reveals that the RequireJS optimizer may be the only way to get decent minification to work, as it seems incapable of selecting alternate paths properly.
Why don't you want to use inbuilt RequireJs optimizer? You may just include this option
optimize : "uglify2"
and all your and third-party code will be minified after concatenation. In this case you don't need to use minified versions of third-party libraries.