In my Webpack configuration, I want to specify a couple attributes that I want to add to my module.
For example, I want to add x and y at build time to MyModule, giving me MyModule.x and MyModule.y.
Is this possible without creating my own Webpack plug-in?
Use provide-plugin
Automatically load modules instead of having to import or require them
everywhere.
new webpack.ProvidePlugin({
identifier: ['module1', 'property1'],
// ...
});
this is class definion
declare class ProvidePlugin {
constructor(definitions: Record<string, string | string[]>);
definitions: Record<string, string | string[]>;
/**
* Apply the plugin
*/
apply(compiler: Compiler): void;
}
Looks like if you want to pass multiple values, you need to pass inside an array.
that pluign is used to add polyfills in webpack5 manually
new webpack.ProvidePlugin({
process: "process/browser",
Buffer: ["buffer", "Buffer"],
React: "react",
}),
Related
So I’m trying to use the pino-clf library in my koa TS project.
I keep getting this when I try to compile:
TSError: ⨯ Unable to compile TypeScript:
src/modules/logger/index.ts:5:21 - error TS7016: Could not find a declaration file for module 'pino-clf'. '/dev/webservices/node_modules/pino-clf/index.js' implicitly has an 'any' type.
Try `npm i --save-dev #types/pino-clf` if it exists or add a new declaration (.d.ts) file containing `declare module 'pino-clf';`
5 import pinoClf from 'pino-clf'
~~~~~~~~~~
pino-clf doesn’t have a def file and there’s no #types/pino-clf available.
I tried adding a pino-clf.d.ts file in the folder of the file that I’m importing the lib into w/ declare module 'pino-clf' in it. While that got the red squigglies in my IDE to go away, TS still refuses to compile.
How in the world do we use use a lib that’s just plain JS w/ TS and w/o adding a ts-ignore?
So there are two ways this can be accomplished.
Solution One: Probably the easiest
You can just use require ex: const pinoClf = require("pinoClf") - the down-side is that dot reference or intellisense isn't available but if you know the methods you want to use its no biggie.
Solution Two:
Create you own typeDef file in the root of you project. For example,
pino-clf.custom.d.ts
declare module "pino-clf.custom" {
const pinoClfJs = require("pinoClf");
export default class pinoClf {
commonLog (type: string, dest: NodeJS.WriteStream, ancillary: any): void {
pinoClfJs.commonLog(type, dest, ancillary);
}
}
}
then in you tsconfig.json file include the new typeDef file:
{
... // assuming src is already there
"include": [
"src", "pino-clf.custom.d.ts"
]
}
after that you can simply import it import pinoClf from "pino-clf.custom";
This is a very basic implementation, and recommend researching if you desire something more complex. Of course there is more than one way to solve a problem but, I hope this helped. Cheers.
This is the specific case I've been working on, below it, further down, is the original more general question.
I'm trying to create a private npm package for e2e testing of a website.
I need to use the webdriverio package.
I want the user to be able to start my executable with a provided configuration, one part of this configuration must be a webdriverio config (actually a subset of it, since I'd like to restrict some options). The other part of the configuration is related to my package: user login name etc.
To achieve this, I've declared my own type and expect the user's argument to conform to it.
But the problem is that when I try to compile the code, which declares that type, I get errors like:
Argument of type '{ … } is not assignable to parameter of type 'RemoteOptions'
Types of property 'logLevel' are incompatible
Type 'string' is not assignable to type 'WebDriverLogTypes | undefined'.
The code, which declares that type is (I left the comments just to demonstrate attempts I'd tried to fix this):
//import { WebdriverIO } from '#wdio/types';
type TestUserConfig = {
//wdioConfig: WebdriverIO,
wdioConfig: {
path: '/wd/hub',
automationProtocol: 'webdriver',
//logLevel: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent',
logLevel: string,
waitforTimeout: number,
capabilities: {
browserName: 'chrome',
},
},
domain: string,
userLogin: string,
userPassword: string,
googleAuthSecret: string,
expectedRole: string,
}
export type {
TestUserConfig,
}
The problem is that if I try to refer to those webdriverio types directly, like:
logLevel: WebDriverLogTypes,
I get errors like Cannot find name 'WebDriverLogTypes'
If I try to import that type like:
import {WebDriverLogTypes} from '#wdio/types';
I ger errors like: Module '"#wdio/types"' has no exported member 'WebDriverLogTypes'
The original question:
Suppose there is a nodejs package (I won't call it module, since I mean a module, but not a module of mine, which is under my control, but a module installed via npm). That package exports a function, which expects certain type as its argument:
type Foo = 'foo';
export default function otherModule(arg: Foo) {}
Now, when I use this module, I want typescript to correctly catch type errors, so that an attempt to compile this TypeScript code throws an error:
import otherModule from 'other-module';
otherModule('bar');
What would be the commonly accepted (probably the best) approach to achieve this?
I've seen some packages have '#types/*' packages too, but I haven't been able to figure out how to use them.
A general approach to use types for other imported libraries/packages etc.:
If the imported library has been developed in JavaScript it, most likely, doesn't have any type definitions out of the box, so in this case there are only these solutions:
check if there is a type definition for this library (usually it may be the '#types/{libraryName}' package or something else, if it's provided in the library docs.
create a type declaration yourself for this library: see the Ambient declarations in the TypeScript docs.
Then import that declaration and use it to refer to types from that library.
For my specific case with webdriverio, I've ended up with the next solution.
Since webdriverio is apparently developed with TypeScript it already has its own type declarations.
So I just looked into the #wdio/types package to see where is that WebDriverLogTypes type had been declared and used that namespace to reference that type:
import type { Options } from '#wdio/types';
type TestUserConfig = {
wdioConfig: {
path: '/wd/hub',
automationProtocol: 'webdriver',
logLevel: Options.WebDriverLogTypes,
waitforTimeout: number,
capabilities: {
browserName: 'chrome',
},
},
domain: string,
userLogin: string,
userPassword: string,
googleAuthSecret: string,
expectedRole: string,
}
export type {
TestUserConfig,
}
I have a folder /resources/js/stubs. In that folder sits a few files, lets say User.stub, Controller.stub and Migration.stub. I would like to use the content of these files in my javascript by doing something like this
import Templates from './Templates.js'
console.log(Templates.User)
// The content of User.stub is printed
I don't want to use this approach
module.exports = name => `
...
`
I could also use the backend to load the files into the view but I would prefer not to.
So then that requires preprocessing. Can I do it with Laravel mix somehow? If not what are my options, what do I need to add to my Laravel app?
partial solution
Thanks to Diogo Sgrillo for pointing out this solution in the comment.
Install raw-loader
yarn add raw-loader --dev
webpack.mix.js
Add this configuration (In my case all the files will be named with a .stub extension.):
mix.webpackConfig({
module: {
rules: [
{
test: /\.stub$/i,
use: 'raw-loader',
},
],
},
});
Also add a separate pipe in webpack.mix.js like this:
mix.js('src/templates/index.js', 'src/templates.js')
It will compile a list of templates in index.js file and put them in templates.js.
src/templates/index.js
// Dynamically load all stubs using raw-loader (see webpack.mix.js)
let stubs = require.context('./', true, /\.stub$/i);
// Create a object with the filename as key and content as value
exports.stubs = stubs.keys().reduce((result, key) => {
return {
[key.replace(/\.\//,'').replace(/\.stub$/,'')] : stubs(key).default,
...result
}
}, {});
export default { /* dont remove this export default or it will break !?? */}
Later it can be used like this:
let templates = require('./templates.js')
console.log(templates['User.php'])
Please comment or add another answer on how to do this more smooth. What is the difference between exports/export? I cant use import with this method, only require and it breaks if I try to export default (or remove export default.
When trying to use import() with webpack using one of our components, I end up getting back an object like this instead of the module:
{
__webpackChunkName: "_nteract_notebook_app_component_6bdbaebb993d75f174e800c88ff17fc7"
}
Example code:
import("#nteract/notebook-app-component").then(App => {
// Rely on App!
// ...
// Except not, because we've got that strange little webpack object
})
Which is in steep contrast to how lodash loads, which provides the real module.
import("lodash").then(_ => {
// Full blown lodash here!
})
If I look at the generated chunk file, it does have javascript in it. One thing (that might be a red herring) is that some modules have /*! no static exports found */ in them yet have code as well. Here's the first 10 lines of the chunk:
$ head -n 10 dist/chunks/_nteract_notebook_app_component_6bdbaebb993d75f174e800c88ff17fc7.js
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([["chunks/_nteract_notebook_app_component_6bdbaebb993d75f174e800c88ff17fc7"],{
/***/ "../../../packages/dropdown-menu/src/index.js":
/*!************************************************************************************************!*\
!*** /Users/kylek/code/src/github.com/nteract/nteract-ext/packages/dropdown-menu/src/index.js ***!
\************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
After that it's a big chunk of evaled code this is the actual module.
This is a result of using the next.js preset for babel, due to their custom handling of import(). They create an object with the webpackChunkName to be used in a lookup later using their dynamic() function.
In order to allow dynamic imports with webpack (and babel), you need to not use the next/babel preset on non-next.js apps and add the syntax-dynamic-import plugin to your babelrc.
npm install --save-dev babel-plugin-syntax-dynamic-import
Example babelrc:
{
"presets": ["env", "react"],
"plugins": [
"syntax-dynamic-import",
"styled-jsx/babel"
]
}
I have a requireJs-based library, that exports object T, which holds Leaflet object L. And I have a Leaflet plugin (this, in my case, but valid for any other), which adds some functions to the global object L. And I have my module, where I want to use object T with extended L. Question: how to gracefully do it with Webpack? I could modify the plugin, to import L from the first library, but anyway don't understand how to plug it in to my module. And not sure that this is the best way.
P.S. I saw this thread, and this as well, but didn't find a good way there. I don't want to use additional < script> tags, and this is quite rambled to have statements like this:
import 'my.leflet.library';
import 'leaflet.plugin';
somewhere in the source - instead, I would prefer to add something to the configuration, that will extend L object right after 'my.leaflet.library' would be loaded, and any module, that would import 'my.leaflet.library' will have T with modified L. Is it possible?
This is my webpack config for Vue (vue.config.js), you can probably adapt it for your needs.
const webpack = require('webpack')
module.exports = {
configureWebpack: {
plugins: [
new webpack.ProvidePlugin({
L: 'leaflet',
'window.L': 'leaflet',
})
],
}
}