Environment variables in svelte + rollup - javascript

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.

Related

Obsidian.md Plugins: ''obsidian' not defined'

I am currently writing a plugin for obsidian.md that renders math functions inside a code block. I used webpack for bundling node libraries like yaml and function-plot. In the config, I added 'obsidian' as an external. The plugin builds with a warning about the bundle size but that doesn't matter since it's local anyways. When I add the plugin, it always says ''obsidian' not defined'. I guess it's looking for obsidian in the global context and can't find it? Here's the repo: https://github.com/leonhma/obsidian-functionplot
Do you know how to configure webpack properly? There's probably some really easy fix but I'm also new to typescript, webpack and developing plugins for obsidian..
Thank you #håkon-hægland for your suggestion (why didn't I think of that?). First of all, the file generated by webpack looked like
function(obsidian) {
... (around 300kb code)
}(obsidian);
, so webpack tried to access some global object called 'obsidian'. The important part in webpack.config.js was
...
externals: [
obsidian: 'obsidian'
],
...
As per your suggestion, i took a look at the other repo, and they use
...
externals: [
obsidian: 'commonjs2 obsidian'
],
...
That fixed my problem and now obsidian is properly imported at runtime. Just posting this in case someone else has this problem, as i couldn't find an existing answer myself.
PS: For those interested, since you are most certainly developing obsidian plugins: It was also really important to set output.libraryTarget to commonjs or commonjs2 inside the webpack config.

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.

Requiring normal javascript file from node compiled script?

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.

Setting environment configuration in a TypeScript app with Webpack

I have gone through several solutions including the ones listed here:
Environment Variables in an isomorphic JS app: Webpack find & replace?
Passing environment-dependent variables in webpack
I am used to using something like gulp-replace-task to find and update a config file for the app to replace things like ##SERVER_URL with something set from the environment.
That way I can do export SERVER_URL=something or run the script with SERVER_URL=something gulp build to set the configuration.
I've tried all of the following:
Using the transform-loader plus envify
This is a suggestion from the first question, but it does not work for me because of:
Module build failed: Error: Parse Error: Line 1: Illegal import declaration
at throwError (ngapp/node_modules/esprima-fb/esprima.js:2823:21)
Seems like esprima-fb is using an import declaration that Webpack can't use for some reason or another. The project is no longer maintained either, so this may be the wrong road to go down.
Using DefinePlugin
I've added:
module: {plugins: [new webpack.DefinePlugin({"process.env.SERVER_URL": "something"})]}
This seems to be ignored, or at least process.env.SERVER_URL does not get interpolated in my typescript files. When I console.log(process.env), it emits an empty object.
Setting using --define for webpack
I updated my npm script:
"start": "webpack-dev-server --define process.env.SERVER_URL=${SERVER_URL}"
However this just ends up replacing process.env.SERVER_URL in my code with a literal "${SERVER_URL}" rather than being interpolated in the npm script.
Is there a simple / convenient (or at this point really any) way to use environment variables in TypeScript apps built with Webpack?
My Webpack setup is essentially what is listed in the Angular docs.

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