Requiring normal javascript file from node compiled script? - javascript

Firstly, i want to state i'm very new to anything to do with node...
Before i state my issue, here is some example code to refer to.
test.js
//test.js
const myMessage = 'Bananas';
export default myMessage; // Im not to sure about this line (problem)
main.js
//main.js
const test = require('./test.js');
console.log(test.myMessage);
I want to require a normal external javascript script called test.js from a node compiled script called main.js. I have compiled main.js simply by typing node main.js in my terminal. But node spat out an error 'Unexpected token export'. I know I'm doing something wrong here. Do i use "Modules"? How do i exclude the export statement?
Thanks for reading, sorry if my problem is making people facepalm on how dumb this issue might seem.

I think the external file you are trying to require is esModule. Such files can't be directly required unless you transpile them to commonJSModule. You have two solutions.
Transpile esModule to commonJSModule using babel. (or change export to module.exports in your test.js)
If you use new node version you can change it's extension to .mjs (rename test.js to test.mjs)
Please take a look at this Medium article which should help.
https://medium.com/#giltayar/native-es-modules-in-nodejs-status-and-future-directions-part-i-ee5ea3001f71

The export syntax is not yet supported in Nodejs (its in an alpha version), instead Nodejs provides a global object* (module.exports) which is what you get back with the require() call, so you just have to set that objects property to the thing you want to export:
const myMessage = 'Bananas';
module.exports.myMessage = myMessage;
or shorter:
exports.myMessage = 'Bananas';
*global in the sense of "it exists although you haven't defined it", actually for every script that gets executed, a new module object will be created that can only be accessed inside of that script.

Use babel register: https://babeljs.io/docs/en/babel-register
npm install #babel/core #babel/register #babel/preset-env --save
And require it in your main.js:
require('#babel/register')({
presets: [
[
'#babel/preset-env',
{
targets: {
node: true
}
}
]
],
ignore: [
/node_modules/
]
});
This will parse other required files through babel which are not in node_modules, so ES6 import/export will work, and it will also polyfill features not present in your current version of node (node: true).
Note this should only be used if you have to require front-end scripts you can't reasonably modify. It's heavyweight to parse every require so if you do have to, make ignore as strict as possible (or even better use the only option) so you're only parsing what you need.

Related

Environment variables in svelte + rollup

I'm looking for a straightforward way to set up environments. I.E. It would be great if I could run npm run dev:local and npm run dev:staging which load different environment files which are accessible at runtime via process.env. In understand it's compiled so I may have to access the variables in a different way. I'm using svelte with rollup straight from sveltejs/template. It should be simple but I see no way of doing it. It's cumbersome, but possible to do with webpack. Is there a simple way to do this?
You can inject build time constants in compiled code with #rollup/plugin-replace.
Something like this:
rollup.config.js
import replace from '#rollup/plugin-replace'
...
const production = !process.env.ROLLUP_WATCH
export default {
...
plugins: [
replace({
'process.env': production ? '"production"' : '"dev"',
}),
...
]
}
Note the double quotes of the value: '"production"'. The plugin injects the string as is in the code so, if you want a string, you need quotes in the quotes.
Also, as mentioned in the plugin's docs, it should be put at the beginning of your plugins array to enable optimizations like shaking out dead code by other plugins that follows it.

How do you get webpack to *actually* ignore an external and rely on the browser to import?

I'm trying to get webpack to ignore an import, so that its imported by the browser using a native ES6 import statement, not webpack. I'm trying to get ffmpeg.js to import directly as it crashes webpack when it tries to bundle it, as the files are too large.
Following the answer here (How to exclude a module from webpack, and instead import it using es6), I have my code in the local tree as /ffmpeg/ffmpeg-mpeg.js and verified my dev server can access as http://localhost:8080/ffmpeg/ffmpeg-webm.js
I then import via:
import ffmpeg from '/ffmpeg/ffmpeg-webm.js';
And add that to the externals section of my webpack config:
externals: {
'/ffmpeg/ffmpeg-webm.js': 'ffmpeg',
},
The result is an link that looks like this
webpack:///external "ffmpeg"
containing:
module.exports = ffmpeg;
Which then fails with "Uncaught Error: Cannot find module ?" (In fact that error is hardcoded in the generated file)
So that seems to assume there is a global ffmpeg option and then maps that module to that, but instead I want it leave the line completely untouched by webpack and leave it to the browser.
Whats the correct way to do that? The exclude rule thats downvoted on that page doesn't work either.
Edit:
You can use this:
import(/* webpackIgnore: true */'/ffmpeg/ffmpeg-webm.js').then(({default: ffmpeg}) => {
//Do what you want to do with ffmpeg
});
Which will prevent webpack from compiling the import (so it will be a regular ES6 import)
Original answer:
You forgot to include the external script in your page.
Also since you pointed out that your file is very big, I'd recommend to include it defered
So you need to add
<script src="/ffmpeg/ffmpeg-webm.js" defer></script>
To the head of your app and you would then import it slightly differently using the import function with a callback
import('/ffmpeg/ffmpeg-webm.js').then(ffmpeg => {
//Do what you want to do with ffmpeg
});
Small note: the externals key does not need to be the path of your file, it's just the name you will use when importing, so rename it if you are getting confused with the path
module.export = {
//...
externals: {
"ffmpeg-webm": "ffmpeg"
}
}
//Then import
import('ffmpeg-webm').then(ffmpeg => {
//Do what you want to do with ffmpeg
});
Alternatively for node js, instead of using externals you could use
const ffmpeg = __non_webpack_require__('/ffmpeg/ffmpeg-webm.js')
Just keep in mind that this will transform it as a normal require that only works with node js

Exporting outside webpack

This is just something I thought today and I didn't see a lot of information so I'm going to share this weird cases and how I personally solved them (if there's a better way please comment, but meanwhile this might help others ^^)
In a regular module, you would do something like this to export your function/library/object/data:
// regular NodeJS way:
module.exports = data;
// ES6 way
// (will get transpiled to the regular way using the module variable by webpack)
export data;
default export data;
When compiling the library usually babel or tsc are used, but if for any reason you want not only to compile (transpile) your library but also pack it using webpack, you will encounter this case.
As you know, in a webpack bundle the module variable is local to the bundle (every module/file gets wrapped with a function where module is a parameter = local variable), so nothing really gets exported outside the bundle, is just nicely managed by webpack.
That means that you can't also access the contents using the regular require/import methods.
In some case you might find necessary to export outside webpack. (i.e. you are trying to build a library using webpack and you want it to be accessible by other people). This basically means you need to access the original module variable, but webpack doesn't expose it like it happened with __non_webpack_require__.
See also: Importing runtime modules from outside webpack bundle
The solution is to create our own __non_webpack_module__ (as webpack does with __non_webpack_require__.
How I did it is using webpack.BannerPlugin to inject some code outside the bundle. This code is prepended to the build after the minification is done, so it's preserved safely.
In your webpack.config.js:
plugins: [
new BannerPlugin({
raw: true,
banner: `const __non_webpack_module__ = module;`,
}),
]
And again, if you are using TypeScript, in global.d.ts:
declare const __non_webpack_module__: NodeModule;
And now, you can do something like this in your code:
__non_webpack_module__.exports = /* your class/function/data/whatever */
This will allow to import it as usual from other files
Tip: You might want to look at BannerPlugin to check other options, like include or exclude so this variable is only generated on the desired files, etc.

In Rollup How do I get --external to work on the command line directly pointing to the module to exclude? (no node resolve plugin)

The documents talk about using external in the context of the node resolve plugin, but I am not using that. I would like to exclude lit-html (which is native es6 modules) so that those imports remain in the bundle.
In my module I import them with import { html, render } from '../../node_modules/lit-html/lit-html.js'; and it works great in the browser.
I have tried every permutation of path including relative path like rollup --format=esm --file=dist/bundle.js -- src/main.js --external 'node_modules/lit-html/lit-html.js' and just get [!] Error: Could not resolve entry (--external).
It does not even say if the file is found, never mind what the problem is.
Seems your command is wrong, use -i to indicate the input file or try moving -- src/main.js to the end of the command without the dashes.
Regarding the external part, don't think it will work without using the exact id of the import but worth a try.
Using a config file:
module.exports = {
input: 'src/main.js',
external:[
'../../node_modules/lit-html/lit-html.js'
],
output: {
format: 'esm',
file: './dist/bundle.js',
sourcemap: true
}
}

Webpack importing video.js returns an empty object

I am trying to use video.js via webpack.
I installed video.js via npm - npm install video.js --save-dev
In webpack I read that video.js should be loaded via script loader else it throws an error.
This is how I am loading video.js through the babel loader
module:
loaders: [
{
test: /video\.js/,
loader: 'script'
}
]
I got this solution from here https://github.com/videojs/video.js/issues/2750
This is my import statement
import videojs from 'video.js';
The issue that I now face is the import is returning an empty object, so when I try to do this:
var vidTag = ReactDOM.findDOMNode(this.refs.html5Video);
this.videojs = videojs(vidTag);
I get this error:
renderer-0.js:8031 Uncaught (in promise) TypeError: (0 , _video2.default) is not a function(…)
Any help will be much appreciated. I am new to ES6 / React / Webpack
Please take a look at the loader's README before copy&pasting some random code. The script-loader is not appropiate here, because it imports scripts into the global scope while skipping the whole module system.
So, if you wanted to use the script-loader, you would just write:
import "script-loader!video.js";
console.log(videojs); // should be an object now
Usually I would not recommend the use of the script-loader because it neglects the whole point of a module system where you import stuff explicitly into the local scope. In the example above, the import happens as a side-effect into the global scope which is effectively the same as just using a <script> tag with all its downsides like name clashes, etc.
There are often better alternatives to it, like the exports-loader, which appends a module.exports at the end of the module, thus turning an old-school global script into a CommonJS module.
In this particular case, however, you don't need a loader at all because video.js is already aware of a CommonJS module system. Just write import videojs from "video.js";.
There is another minor problem, however. If you compile this with webpack, it will print a warning to the console:
WARNING in ../~/video.js/dist/video.js
Critical dependencies:
13:480-487 This seems to be a pre-built javascript file. Though this is possible, it's not recommended. Try to require the original source to get better results.
# ../~/video.js/dist/video.js 13:480-487
This is because webpack detects that this file has already been bundled somehow. Often it's better to include the actual src with all its tiny modules instead of one large dist because this way webpack is able to optimize the bundle in a better way. I've written down an exhaustive explanation about how to import legacy scripts with webpack.
Unfortunately, video.js does not include its src in the version deployed at npm, so you're forced to use the dist. In order to get rid of the error message and to improve webpack's build time, you can instruct webpack to skip video.js when parsing the code for require() statements by setting the module.noParse option in your webpack.config.js:
module: {
noParse: [
/node_modules[\\/]video\.js/
]
}
Usually it's safe to flag all pre-bundled modules (typically those with a dist folder) as noParse because they are already self-contained.
include SDN
<script src="//vjs.zencdn.net/5.11/video.min.js"></script>
webpack config:
config.externals = {
'video.js': 'videojs'
};

Categories