Keep a ES module outside the bundle - javascript

My application has a configuration file
external-config.js
import {constants} from "./constants.js";
export const ExternalConfig = {
title: "My application",
version: "2.0",
constants: constants
list: ["uno", "due", "tre"]
}
I don't want it to be bundled with the application, I want to keep it outside. I tried the IgnorePlugin, but apparently this simply breaks the dependency graph and I get the error ReferenceError: Config is not defined even if the reference to the path of the config file in the budle is correct.
plugins: [
new webpack.IgnorePlugin({
checkResource (resource) {
if (resource === "./conf/external-config.js") return true;
return false;
}
})
]
I cannot even import it in the main html page like
<script type="module" src="./conf/config.js"></script>
because in this way I couldn't access the object outside its own script.
Is there a way to do that?
EDIT: following #raz-nonen advice, I tried null-loader, it seems it could be a solution. The problem with that is the physical position of the configuration file.
rules: [
{
test: path.resolve(__dirname, "src/conf/external-config.js"),
use: "null-loader"
}
...]
And this is the result in the built script
// EXTERNAL MODULE: ./src/conf/external-config.js
var external_config = __webpack_require__(13);
But the actual position of the configuration in the dist folder is ./conf/external-config.js, not ./src/conf/external-config.js
This is the chunk of my app that consumes the external file
import {ExternalConfig} from "./conf/external-config.js";
class MyApp extends LitElement {
constructor() {
super();
console.log(ExternalConfig.list)
}
}

You'll need to make this file available in the dist folder. You can do that with copy-webpack-plugin
Tell webpack that ExternalConfig will be imported from external. It means that you'll have to take care that it'll be available at runtime. You can do it simply by importing your conf/config.js that you copied from a <script> tag in your index.html.
Add:
externals: {
'conf/external-config': 'conf/external-config'
}
In your webpack configuration.

Related

How to create Vite based Vue component library consumable via .mjs files?

I created a Vite based Vue library. This library should distribute a single component inside a .mjs file.
Library setup
I basically created a new Vue app via npm create vue, moved every dependency to dev dependencies and added this to the package.json
"files": [
"dist"
],
"exports": {
".": {
"import": "./dist/computedRenderer.mjs",
"require": "./dist/computedRenderer.umd.cjs"
}
},
After that I added the following to my vite.config.ts file
build: {
lib: {
name: "mylib",
fileName: "computedRenderer",
entry: resolve(__dirname, './src/myComp/index.ts'), // index file exporting my component
},
rollupOptions: {
external: ["vue"],
output: {
globals: {
vue: "Vue",
},
},
},
}
Building the project should generate you a computedRenderer.mjs in the dist directory ( as expected ).
File server for testing purposes
I created a small Node server serving the static .mjs file
import cors from "cors";
import express from "express";
const port = 3_000;
express()
.use(express.json())
.use(cors())
.use(express.static('./public')) // directory contains the generated .mjs file
.listen(port, () => {
console.log(`Listening on port ${port}`);
});
When running the server and calling
http://localhost:3000/computedRenderer.mjs
you should get the file content
How I consume the component
In my main project I try to import the component dynamically during runtime like so
<script setup lang="ts">
import { onMounted } from "vue";
onMounted(async () => {
const source = 'http://localhost:3000/computedRenderer.mjs'; // example url
const { ComputedRenderer } = await import(source);
// ...
});
</script>
Unfortunately I get the following warning
The above dynamic import cannot be analyzed by Vite. See https://github.com/rollup/plugins/tree/master/packages/dynamic-import-vars#limitations for supported dynamic import formats. If this is intended to be left as-is, you can use the /* #vite-ignore */ comment inside the import() call to suppress this warning.
and error
Uncaught (in promise) TypeError: Failed to resolve module specifier "vue". Relative references must start with either "/", "./", or "../".
Is my library setup wrong? Or how can I consume the component via url?
Please let me know if you need more information

Webpack is erroring when I attempt to import a directory containing modules

I'm trying to create a small npm library to make interfacing with an API a little neater. My folder structure is as follows...
dist/
index.js
src/
index.js
endpoints/
endpoint1.js
package.json
webpack.config.js
Within my src/index.js file I have..
import {endpoint1} from './endpoints'
module.exports = class lib {
...
}
When I npm run build (which runs webpack --display-error-details --mode production) webpack throws a big error saying "Module not found: Error: Can't resolve './endpoints' in 'my\project\dir\src'.
My webpack.config.js file currently looks like...
const path = require('path');
module.exports = {
mode: 'production',
entry: path.join(__dirname, '/src/index.js'),
output: {
path: path.resolve('dist'),
filename: 'index.js',
libraryTarget: 'commonjs2'
},
module: {
rules: [
{
test: /.js?$/,
exclude: /(node_modules)/,
use: 'babel-loader'
}
]
},
resolve: {
modules: [
path.resolve(__dirname, 'src/endpoints')
],
extensions: ['.js']
}
};
I can see similar questions have been asked before and the resolutions listed don't seem to work for me so I thought I'd post it incase im making a rookie error. If any more info is required just say! Sorry if it's fairly wall of texty. Thanks.
The correct import would be:
import endpoint1 from 'endpoint1';
By using resolve.modules you tell Webpack to look up non relative paths in that folder. The module name is "enpoint1".
But actually you should only do this with libraries that you use across your project, for an endpoint a relative import will be appropriate:
import endpoint1 from "./endpoints/endpoint1";
import {endpoint1} from './endpoints' means this:
import from file ./endpoints/index.js something that is exported under the name enpoint1 in that file. If you import directory then it refers to index.js under that directory, not to all other files. It doesn't exist in your setup.
Names inside {} refer to named imports. This goes only for es6 modules-style imports like import {...} from. If you ommit {} then you import the default. CommonJs-style imports like const {...} = require('') work differently. CommonJs does not have named imports and exports. It just will import default from that file and then fetch a field via object destructuring.
What you export is something unnamed(i.e. default) from file ./endpoints/enpoint1.js
Something is unnamed because you use module.exports = which is CommonJS-style export. CommonJS does not support named exports. This is equevalent to export default class lib ... in es6 modules-style exports.
IF you want to import many files under directory you can consider these solutions:
1) Often single import points are created. You make a index.js file. In it you import manually every file under the directoy that you want to export. Then you export it under names. Like this:
import a from './a.js';
import b from './b.js';
import c from './c.js';
export { a, b, c };
Then it will work
2) In some rare cases in might make sence to use fs.readdir or fs.readdirSync to scan the entire directory and dynamicly require files in a loop. Use it only if you must. E.g. db migrations.

Set context dir for import when include some file in entry.js?

In entry.js file I have :
import 'index.js';
And that index.js has many imports from another dir like :
import test from 'modulename'.
But I have moved all modules to another dir and want to keep just index.js in main dir. So that's why I must rewrite all imports with new dir location like this :
import test from ../anotherdir/modulename
How to get rid of this and force webpack to search modules in new dir?
Now I am using webpack allias and this help me if path is too long but it still require to change all imports.
You can use resolve alias in webpack
Resolve
These options change how modules are resolved. webpack provides
reasonable defaults, but it is possible to change the resolving in
detail. Have a look at Module Resolution for more explanation of how
the resolver works.
webpack.config.js
module.exports = {
//...
resolve: {
alias: {
Utilities: path.resolve(__dirname, 'src/utilities/'),
Templates: path.resolve(__dirname, 'src/templates/')
}
}
};
Now, instead of using relative paths when importing like so:
import Utility from '../../utilities/utility';
you can use the alias:
import Utility from 'Utilities/utility';

Using expose-loader with webpack 2 to load a module

I have jwplayer in my lib/ folder because no node_module exists. I tried to use expose-loader in order to be able to import it. In my webpack, I have the following basic setup in order to get this to work:
const path = require('path');
module.exports = {
// ...
module: {
rules: [{
test: /\.jwplayer\.js$/,
use: {
loader: 'expose-loader',
options: 'jwplayer', // have also tried `options: { name: 'jwplayer' }`
},
}]
},
resolve: {
alias: {
jwplayer: path.join(__dirname, './lib/jwplayer-7.7.4/jwplayer.js'),
}
},
externals: {
window: 'Window',
}
};
The strange thing is, jwplayer is exposed on the window object, but it is not available as a module.
import jwplayer from 'jwplayer';
console.log(jwplayer); // Object {} (not jwplayer)
console.log(window.jwplayer); // function jwplayer() {}
Am I loading it incorrectly? How should I load in jwplayer with webpack 2?
That's not how you use the expose loader. The expose loader tells to webpack to expose something to the global context when the bundle is loaded. My understanding is that you want to use jwplayer inside the bundle itself.
You can use the script-loader, that's how I usually import scripts (analytics, for instance)
Actually you can use
externals: ['jwplayer'],
Because externals is for passing global variables inside the bundle to be able to use them as a dependency and then you can import your library as any other
import jwplayer from 'jwplayer';
webpack documentation

Webpack export function

I have some js files, and each file is a standonlone function with unique name, And I want to pack all this files in one bundle So I do this code
module.exports = {
entry: {
app:[
'./file1.js',
'./file2.js',
'./file3.js',
'./file4.js'
],
},
output: {
path: './dist/',
filename: '[name].bundle.js'
}
};
that's work and I have my bundle file ".dist/app.bundle.js"
Now I have some js code in the HTML body that need to call functions in the bundle,
If I try to call function "functionA" (that is difined in file1.js) I get this message in browser console
Uncaught ReferenceError: functionA is not defined
The question is how can I export my functions from bundle to import it in my HTML code ?
Exporting things from an entry point file does not make them available to the global scope. You have two options - either explicitly add the functions to the window object, like so:
window.functionA = functionA;
Or configure your build to output as a library:
// webpack.config.js - abridged
module.exports = {
output: {
library: "myModule",
libraryTarget: "umd"
}
};
I don't know how the latter interacts with your entry point being set to an array of files - you may have to make a single entry file (main.js or something) that imports all of the functions and then re-exports them contained in an object, or something like that.

Categories

Resources