1667178300
Despite all the recent hype, setting up a new TypeScript (x React) library can be tough. Between Rollup, Jest, tsconfig
, Yarn resolutions, ESLint, and getting VSCode to play nicely....there is just a whole lot of stuff to do (and things to screw up). TSDX is a zero-config CLI that helps you develop, test, and publish modern TypeScript packages with ease--so you can focus on your awesome new library and not waste another afternoon on the configuration.
TSDX comes with the "battery-pack included" and is part of a complete TypeScript breakfast:
invariant
error codestsdx test
tsdx lint
.babelrc.js
, jest.config.js
, .eslintrc.js
, and tsdx.config.js
npx tsdx create mylib
cd mylib
yarn start
That's it. You don't need to worry about setting up TypeScript or Rollup or Jest or other plumbing. Just start editing src/index.ts
and go!
Below is a list of commands you will probably find useful:
npm start
or yarn start
Runs the project in development/watch mode. Your project will be rebuilt upon changes. TSDX has a special logger for your convenience. Error messages are pretty printed and formatted for compatibility VS Code's Problems tab.
Your library will be rebuilt if you make edits.
npm run build
or yarn build
Bundles the package to the dist
folder. The package is optimized and bundled with Rollup into multiple formats (CommonJS, UMD, and ES Module).
npm test
or yarn test
Runs your tests using Jest.
npm run lint
or yarn lint
Runs Eslint with Prettier on .ts and .tsx files. If you want to customize eslint you can add an eslint
block to your package.json, or you can run yarn lint --write-file
and edit the generated .eslintrc.js
file.
prepare
scriptBundles and packages to the dist
folder. Runs automatically when you run either npm publish
or yarn publish
. The prepare
script will run the equivalent of npm run build
or yarn build
. It will also be run if your module is installed as a git dependency (ie: "mymodule": "github:myuser/mymodule#some-branch"
) so it can be depended on without checking the transpiled code into git.
Aside from just bundling your module into different formats, TSDX comes with some optimizations for your convenience. They yield objectively better code and smaller bundle sizes.
After TSDX compiles your code with TypeScript, it processes your code with 3 Babel plugins:
babel-plugin-annotate-pure-calls
: Injects for #__PURE
annotations to enable treeshakingbabel-plugin-dev-expressions
: A mirror of Facebook's dev-expression Babel plugin. It reduces or eliminates development checks from production codebabel-plugin-rename-import
: Used to rewrite any lodash
importsbabel-plugin-annotate-pure-calls
+ babel-plugin-dev-expressions
work together to fully eliminate dead code (aka treeshake) development checks from your production code. Let's look at an example to see how it works.
Imagine our source code is just this:
// ./src/index.ts
export const sum = (a: number, b: number) => {
if (process.env.NODE_ENV !== 'production') {
console.log('Helpful dev-only error message');
}
return a + b;
};
tsdx build
will output an ES module file and 3 CommonJS files (dev, prod, and an entry file). If you want to specify a UMD build, you can do that as well. For brevity, let's examine the CommonJS output (comments added for emphasis):
// Entry File
// ./dist/index.js
'use strict';
// This determines which build to use based on the `NODE_ENV` of your end user.
if (process.env.NODE_ENV === 'production') {
module.exports = require('./mylib.cjs.production.js');
} else {
module.exports = require('./mylib.development.cjs');
}
// CommonJS Development Build
// ./dist/mylib.development.cjs
'use strict';
const sum = (a, b) => {
{
console.log('Helpful dev-only error message');
}
return a + b;
};
exports.sum = sum;
//# sourceMappingURL=mylib.development.cjs.map
// CommonJS Production Build
// ./dist/mylib.cjs.production.js
'use strict';
exports.sum = (s, t) => s + t;
//# sourceMappingURL=test-react-tsdx.cjs.production.js.map
AS you can see, TSDX stripped out the development check from the production code. This allows you to safely add development-only behavior (like more useful error messages) without any production bundle size impact.
For ESM build, it's up to end-user to build environment specific build with NODE_ENV replace (done by Webpack 4 automatically).
TSDX's rollup config removes getters and setters on objects so that property access has no side effects. Don't do it.
babel-plugin-dev-expressions
TSDX will use babel-plugin-dev-expressions
to make the following replacements before treeshaking.
__DEV__
Replaces
if (__DEV__) {
console.log('foo');
}
with
if (process.env.NODE_ENV !== 'production') {
console.log('foo');
}
IMPORTANT: To use __DEV__
in TypeScript, you need to add declare var __DEV__: boolean
somewhere in your project's type path (e.g. ./types/index.d.ts
).
// ./types/index.d.ts
declare var __DEV__: boolean;
Note: The
dev-expression
transform does not run whenNODE_ENV
istest
. As such, if you use__DEV__
, you will need to define it as a global constant in your test environment.
invariant
Replaces
invariant(condition, 'error message here');
with
if (!condition) {
if ('production' !== process.env.NODE_ENV) {
invariant(false, 'error message here');
} else {
invariant(false);
}
}
Note: TSDX doesn't supply an invariant
function for you, you need to import one yourself. We recommend https://github.com/alexreardon/tiny-invariant.
To extract and minify invariant
error codes in production into a static codes.json
file, specify the --extractErrors
flag in command line. For more details see Error extraction docs.
warning
Replaces
warning(condition, 'dev warning here');
with
if ('production' !== process.env.NODE_ENV) {
warning(condition, 'dev warning here');
}
Note: TSDX doesn't supply a warning
function for you, you need to import one yourself. We recommend https://github.com/alexreardon/tiny-warning.
If you want to use a lodash function in your package, TSDX will help you do it the right way so that your library does not get fat shamed on Twitter. However, before you continue, seriously consider rolling whatever function you are about to use on your own. Anyways, here is how to do it right.
First, install lodash
and lodash-es
as dependencies
yarn add lodash lodash-es
Now install @types/lodash
to your development dependencies.
yarn add @types/lodash --dev
Import your lodash method however you want, TSDX will optimize it like so.
// ./src/index.ts
import kebabCase from 'lodash/kebabCase';
export const KebabLogger = (msg: string) => {
console.log(kebabCase(msg));
};
For brevity let's look at the ES module output.
import o from"lodash-es/kebabCase";const e=e=>{console.log(o(e))};export{e as KebabLogger};
//# sourceMappingURL=test-react-tsdx.esm.production.js.map
TSDX will rewrite your import kebabCase from 'lodash/kebabCase'
to import o from 'lodash-es/kebabCase'
. This allows your library to be treeshakable to end consumers while allowing to you to use @types/lodash
for free.
Note: TSDX will also transform destructured imports. For example,
import { kebabCase } from 'lodash'
would have also been transformed to `import o from "lodash-es/kebabCase".
After running --extractErrors
, you will have a ./errors/codes.json
file with all your extracted invariant
error codes. This process scans your production code and swaps out your invariant
error message strings for a corresponding error code (just like React!). This extraction only works if your error checking/warning is done by a function called invariant
.
Note: We don't provide this function for you, it is up to you how you want it to behave. For example, you can use either tiny-invariant
or tiny-warning
, but you must then import the module as a variable called invariant
and it should have the same type signature.
⚠️Don't forget: you will need to host the decoder somewhere. Once you have a URL, look at ./errors/ErrorProd.js
and replace the reactjs.org
URL with yours.
Known issue: our
transformErrorMessages
babel plugin currently doesn't have sourcemap support, so you will see "Sourcemap is likely to be incorrect" warnings. We would love your help on this.
TODO: Simple guide to host error codes to be completed
❗⚠️❗ Warning:
These modifications will override the default behavior and configuration of TSDX. As such they can invalidate internal guarantees and assumptions. These types of changes can break internal behavior and can be very fragile against updates. Use with discretion!
TSDX uses Rollup under the hood. The defaults are solid for most packages (Formik uses the defaults!). However, if you do wish to alter the rollup configuration, you can do so by creating a file called tsdx.config.js
at the root of your project like so:
// Not transpiled with TypeScript or Babel, so use plain Es6/Node.js!
module.exports = {
// This function will run for each entry/format/env combination
rollup(config, options) {
return config; // always return a config.
},
};
The options
object contains the following:
export interface TsdxOptions {
// path to file
input: string;
// Name of package
name: string;
// JS target
target: 'node' | 'browser';
// Module format
format: 'cjs' | 'umd' | 'esm' | 'system';
// Environment
env: 'development' | 'production';
// Path to tsconfig file
tsconfig?: string;
// Is error extraction running?
extractErrors?: boolean;
// Is minifying?
minify?: boolean;
// Is this the very first rollup config (and thus should one-off metadata be extracted)?
writeMeta?: boolean;
// Only transpile, do not type check (makes compilation faster)
transpileOnly?: boolean;
}
const postcss = require('rollup-plugin-postcss');
const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');
module.exports = {
rollup(config, options) {
config.plugins.push(
postcss({
plugins: [
autoprefixer(),
cssnano({
preset: 'default',
}),
],
inject: false,
// only write out CSS for the first bundle (avoids pointless extra files):
extract: !!options.writeMeta,
})
);
return config;
},
};
You can add your own .babelrc
to the root of your project and TSDX will merge it with its own Babel transforms (which are mostly for optimization), putting any new presets and plugins at the end of its list.
You can add your own jest.config.js
to the root of your project and TSDX will shallow merge it with its own Jest config.
You can add your own .eslintrc.js
to the root of your project and TSDX will deep merge it with its own ESLint config.
patch-package
If you still need more customizations, we recommend using patch-package
so you don't need to fork. Keep in mind that these types of changes may be quite fragile against version updates.
TSDX was originally ripped out of Formik's build tooling. TSDX has several similarities to @developit/microbundle, but that is because Formik's Rollup configuration and Microbundle's internals had converged around similar plugins.
Some key differences include:
tsdx watch
Description
Rebuilds on any change
Usage
$ tsdx watch [options]
Options
-i, --entry Entry module
--target Specify your target environment (default web)
--name Specify name exposed in UMD builds
--format Specify module format(s) (default cjs,esm)
--tsconfig Specify your custom tsconfig path (default <root-folder>/tsconfig.json)
--verbose Keep outdated console output in watch mode instead of clearing the screen
--onFirstSuccess Run a command on the first successful build
--onSuccess Run a command on a successful build
--onFailure Run a command on a failed build
--noClean Don't clean the dist folder
--transpileOnly Skip type checking
-h, --help Displays this message
Examples
$ tsdx watch --entry src/foo.tsx
$ tsdx watch --target node
$ tsdx watch --name Foo
$ tsdx watch --format cjs,esm,umd
$ tsdx watch --tsconfig ./tsconfig.foo.json
$ tsdx watch --noClean
$ tsdx watch --onFirstSuccess "echo The first successful build!"
$ tsdx watch --onSuccess "echo Successful build!"
$ tsdx watch --onFailure "echo The build failed!"
$ tsdx watch --transpileOnly
tsdx build
Description
Build your project once and exit
Usage
$ tsdx build [options]
Options
-i, --entry Entry module
--target Specify your target environment (default web)
--name Specify name exposed in UMD builds
--format Specify module format(s) (default cjs,esm)
--extractErrors Opt-in to extracting invariant error codes
--tsconfig Specify your custom tsconfig path (default <root-folder>/tsconfig.json)
--transpileOnly Skip type checking
-h, --help Displays this message
Examples
$ tsdx build --entry src/foo.tsx
$ tsdx build --target node
$ tsdx build --name Foo
$ tsdx build --format cjs,esm,umd
$ tsdx build --extractErrors
$ tsdx build --tsconfig ./tsconfig.foo.json
$ tsdx build --transpileOnly
tsdx test
This runs Jest, forwarding all CLI flags to it. See https://jestjs.io for options. For example, if you would like to run in watch mode, you can run tsdx test --watch
. So you could set up your package.json
scripts
like:
{
"scripts": {
"test": "tsdx test",
"test:watch": "tsdx test --watch",
"test:coverage": "tsdx test --coverage"
}
}
tsdx lint
Description
Run eslint with Prettier
Usage
$ tsdx lint [options]
Options
--fix Fixes fixable errors and warnings
--ignore-pattern Ignore a pattern
--max-warnings Exits with non-zero error code if number of warnings exceed this number (default Infinity)
--write-file Write the config file locally
--report-file Write JSON report to file locally
-h, --help Displays this message
Examples
$ tsdx lint src
$ tsdx lint src --fix
$ tsdx lint src test --ignore-pattern test/foo.ts
$ tsdx lint src test --max-warnings 10
$ tsdx lint src --write-file
$ tsdx lint src --report-file report.json
Please see the Contributing Guidelines.
Author: jaredpalmer
Source Code: https://github.com/jaredpalmer/tsdx
License: MIT license
1657832400
This is a bare bones starter for building React/Typescript extensions for Chrome using manifest_version: 3
.
Under the hood, this starter leans pretty heavily on the infrastructure from Extend Chrome. The primary difference is this starter integrates some foundational code and manifest configuration for v3 which I couldn't find in the boilerplate examples they offer.
In my tinkering with this starter there are a few notes that I find helpful.
If you reference files that can't be automatically recognized, use the web_accessible_resources
property in manifest to explicitly bundle them.
"web_accessible_resources": [
{
"resources": [
"pages/popup/index.html",
"pages/popup/App.tsx",
"pages/popup/index.tsx"
],
"matches": [
"<all_urls>"
]
}
]
Though a little hacky, I have had some success generating v2 and v3 manifests with a little customization to this starter. The approach can be seen on my extension Link Roamer.
The premise is pretty simple: hook into a specific string value throughout your app and programmatically fire v2 or v3 API methods depending on the build environment.
In this way, functions can be conditionally compiled.
if ('isV3Manifest') {
return (
await chrome.scripting.executeScript({
target: { tabId },
func,
})
)[0].result
} else {
return (
await browser.tabs.executeScript(tabId, {
code: `(${func})()`,
})
)[0]
}
For development with automatic reloading:
npm run start
Open the Extensions Dashboard, enable "Developer mode", click "Load unpacked", and choose the dist
folder.
When you make changes in src
the background script and any content script will reload automatically.
When it's time to publish your Chrome extension, make a production build to submit to the Chrome Web Store. This boilerplate will use the version in package.json
, unless you add a version to src/manifest.json
.
Make sure you have updated the name and version of your extension in
package.json
.
Run the following line:
yarn build
This will create a ZIP file with your package name and version in the releases
folder.
I like making things. Check out what I'm up to lately.
Author: rossmoody
Source code: https://github.com/rossmoody/ts-extension-starter
License: MIT license
1657141200
npm install --save-dev rollup-plugin-css-only
# If using Node.js lower than 10.12
npm install --save-dev rollup-plugin-css-only@1
// rollup.config.js
import css from 'rollup-plugin-css-only'
export default {
entry: 'entry.js',
dest: 'bundle.js',
plugins: [
css({ output: 'bundle.css' })
]
}
// entry.js
import './reset.css'
import './layout.css'
import Vue from 'vue'
The idea is to keep the options similar to rollup-plugin-sass.
There is 1 option: output
. By default the plugin will base the filename for the css on the bundle destination.
css({
// Filename to write all styles to
output: 'bundle.css',
// Callback that will be called ongenerate with two arguments:
// - styles: the contents of all style tags combined: 'body { color: green }'
// - styleNodes: an array of style objects: [{lang: 'css', content: 'body { color: green }'}]
output: function (styles, styleNodes) {
writeFileSync('bundle.css', styles)
},
// Disable any style output or callbacks
output: false,
// Default behaviour is to write all styles to the bundle destination where .js is replaced by .css
output: null
})
Please see CHANGELOG for more information what has changed recently.
Contributions and feedback are very welcome.
To get it running:
npm install
npm run build
Author: thgh
Source code: https://github.com/thgh/rollup-plugin-css-only
License: MIT license
#css #rollup #javascript
1657040400
🍣 A Rollup plugin which converts ES2015+ code with the Bublé compiler.
This plugin requires an LTS Node version (v8.0.0+) and Rollup v1.20.0+.
Using npm:
npm install @rollup/plugin-buble --save-dev
Create a rollup.config.js
configuration file and import the plugin:
import buble from '@rollup/plugin-buble';
export default {
input: 'src/index.js',
output: {
dir: 'output',
format: 'cjs'
},
plugins: [buble()]
};
Then call rollup
either via the CLI or the API.
transforms
Type: Object
Default: { modules: false }
Specifies additional transform options for the Bublé compiler.
exclude
Type: String
| Array[...String]
Default: null
A minimatch pattern, or array of patterns, which specifies the files in the build the plugin should ignore. By default no files are ignored.
include
Type: String
| Array[...String]
Default: null
A minimatch pattern, or array of patterns, which specifies the files in the build the plugin should operate on. By default all files are targeted.
Author:
Source Code: https://github.com/rollup/plugins/
License:
#vite #typescript #javascript #rollup
1657029600
@rollup/plugin-babel
🍣 A Rollup plugin for seamless integration between Rollup and Babel.
If you're using Babel to transpile your ES6/7 code and Rollup to generate a standalone bundle, you have a couple of options:
Both approaches have disadvantages – in the first case, on top of the additional configuration complexity, you may end up with Babel's helpers (like classCallCheck
) repeated throughout your code (once for each module where the helpers are used). In the second case, transpiling is likely to be slower, because transpiling a large bundle is much more work for Babel than transpiling a set of small files.
Either way, you have to worry about a place to put the intermediate files, and getting sourcemaps to behave becomes a royal pain.
Using Rollup with @rollup/plugin-babel
makes the process far easier.
This plugin requires an LTS Node version (v10.0.0+) and Rollup v1.20.0+.
npm install @rollup/plugin-babel --save-dev
Create a rollup.config.js
configuration file and import the plugin:
import { babel } from '@rollup/plugin-babel';
const config = {
input: 'src/index.js',
output: {
dir: 'output',
format: 'esm'
},
plugins: [babel({ babelHelpers: 'bundled' })]
};
export default config;
Then call rollup
either via the CLI or the API.
@rollup/plugin-commonjs
When using @rollup/plugin-babel
with @rollup/plugin-commonjs
in the same Rollup configuration, it's important to note that @rollup/plugin-commonjs
must be placed before this plugin in the plugins
array for the two to work together properly. e.g.
import { babel } from '@rollup/plugin-babel';
import commonjs from '@rollup/plugin-commonjs';
const config = {
...
plugins: [
commonjs(),
babel({ babelHelpers: 'bundled' })
],
};
This plugin respects Babel configuration files by default and they are generally the best place to put your configuration.
You can also run Babel on the generated chunks instead of the input files. Even though this is slower, it is the only way to transpile Rollup's auto-generated wrapper code to lower compatibility targets than ES5, see Running Babel on the generated code for details.
All options are as per the Babel documentation, plus the following:
exclude
Type: String | RegExp | Array[...String|RegExp]
A minimatch pattern, or array of patterns, which specifies the files in the build the plugin should ignore. When relying on Babel configuration files you can only exclude additional files with this option, you cannot override what you have configured for Babel itself.
include
Type: String | RegExp | Array[...String|RegExp]
A minimatch pattern, or array of patterns, which specifies the files in the build the plugin should operate on. When relying on Babel configuration files you cannot include files already excluded there.
filter
Type: (id: string) => boolean
Custom filter function can be used to determine whether or not certain modules should be operated upon.
Usage:
import { createFilter } from '@rollup/pluginutils';
const include = 'include/**.js';
const exclude = 'exclude/**.js';
const filter = createFilter(include, exclude, {});
extensions
Type: Array[...String]
Default: ['.js', '.jsx', '.es6', '.es', '.mjs']
An array of file extensions that Babel should transpile. If you want to transpile TypeScript files with this plugin it's essential to include .ts
and .tsx
in this option.
babelHelpers
Type: 'bundled' | 'runtime' | 'inline' | 'external'
Default: 'bundled'
It is recommended to configure this option explicitly (even if with its default value) so an informed decision is taken on how those babel helpers are inserted into the code.
We recommend to follow these guidelines to determine the most appropriate value for your project:
'runtime'
- you should use this especially when building libraries with Rollup. It has to be used in combination with @babel/plugin-transform-runtime
and you should also specify @babel/runtime
as dependency of your package. Don't forget to tell Rollup to treat the helpers imported from within the @babel/runtime
module as external dependencies when bundling for cjs
& es
formats. This can be accomplished via regex (external: [/@babel\/runtime/]
) or a function (external: id => id.includes('@babel/runtime')
). It's important to not only specify external: ['@babel/runtime']
since the helpers are imported from nested paths (e.g @babel/runtime/helpers/get
) and Rollup will only exclude modules that match strings exactly.'bundled'
- you should use this if you want your resulting bundle to contain those helpers (at most one copy of each). Useful especially if you bundle an application code.'external'
- use this only if you know what you are doing. It will reference helpers on global babelHelpers
object. Used in combination with @babel/plugin-external-helpers
.'inline'
- this is not recommended. Helpers will be inserted in each file using this option. This can cause serious code duplication. This is the default Babel behavior as Babel operates on isolated files - however, as Rollup is a bundler and is project-aware (and therefore likely operating across multiple input files), the default of this plugin is "bundled"
.skipPreflightCheck
Type: Boolean
Default: false
Before transpiling your input files this plugin also transpile a short piece of code for each input file. This is used to validate some misconfiguration errors, but for sufficiently big projects it can slow your build times so if you are confident about your configuration then you might disable those checks with this option.
Ideally, you should only be transforming your source code, rather than running all of your external dependencies through Babel (to ignore external dependencies from being handled by this plugin you might use exclude: 'node_modules/**'
option). If you have a dependency that exposes untranspiled ES6 source code that doesn't run in your target environment, then you may need to break this rule, but it often causes problems with unusual .babelrc
files or mismatched versions of Babel.
We encourage library authors not to distribute code that uses untranspiled ES6 features (other than modules) for this reason. Consumers of your library should not have to transpile your ES6 code, any more than they should have to transpile your CoffeeScript, ClojureScript or TypeScript.
Use babelrc: false
to prevent Babel from using local (i.e. to your external dependencies) .babelrc
files, relying instead on the configuration you pass in.
In some cases Babel uses helpers to avoid repeating chunks of code – for example, if you use the class
keyword, it will use a classCallCheck
function to ensure that the class is instantiated correctly.
By default, those helpers will be inserted at the top of the file being transformed, which can lead to duplication. This rollup plugin automatically deduplicates those helpers, keeping only one copy of each one used in the output bundle. Rollup will combine the helpers in a single block at the top of your bundle.
You can customize how those helpers are being inserted into the transformed file with babelHelpers
option.
This is not needed since Babel 7 - it knows automatically that Rollup understands ES modules & that it shouldn't use any module transform with it. Unless you forcefully include a module transform in your Babel configuration.
If you have been pointed to this section by an error thrown by this plugin, please check your Babel configuration files and disable any module transforms when running Rollup builds.
You can run @rollup/plugin-babel
on the output files instead of the input files by using getBabelOutputPlugin(...)
. This can be used to perform code transformations on the resulting chunks and is the only way to transform Rollup's auto-generated code. By default, the plugin will be applied to all outputs:
// rollup.config.js
import { getBabelOutputPlugin } from '@rollup/plugin-babel';
export default {
input: 'main.js',
plugins: [
getBabelOutputPlugin({
presets: ['@babel/preset-env']
})
],
output: [
{ file: 'bundle.cjs.js', format: 'cjs' },
{ file: 'bundle.esm.js', format: 'esm' }
]
};
If you only want to apply it to specific outputs, you can use it as an output plugin (requires at least Rollup v1.27.0):
// rollup.config.js
import { getBabelOutputPlugin } from '@rollup/plugin-babel';
export default {
input: 'main.js',
output: [
{ file: 'bundle.js', format: 'esm' },
{
file: 'bundle.es5.js',
format: 'esm',
plugins: [getBabelOutputPlugin({ presets: ['@babel/preset-env'] })]
}
]
};
The include
, exclude
and extensions
options are ignored when the when using getBabelOutputPlugin
and createBabelOutputPluginFactory
will produce warnings, and there are a few more points to note that users should be aware of.
You can also run the plugin twice on the code, once when processing the input files to transpile special syntax to JavaScript and once on the output to transpile to a lower compatibility target:
// rollup.config.js
import babel, { getBabelOutputPlugin } from '@rollup/plugin-babel';
export default {
input: 'main.js',
plugins: [babel({ presets: ['@babel/preset-react'] })],
output: [
{
file: 'bundle.js',
format: 'esm',
plugins: [getBabelOutputPlugin({ presets: ['@babel/preset-env'] })]
}
]
};
Unlike the regular babel
plugin, getBabelOutputPlugin(...)
will not automatically search for Babel configuration files. Besides passing in Babel options directly, however, you can specify a configuration file manually via Babel's configFile
option:
getBabelOutputPlugin({
configFile: path.resolve(__dirname, 'babel.config.js')
});
As getBabelOutputPlugin(...)
will run after Rollup has done all its transformations, it needs to make sure it preserves the semantics of Rollup's output format. This is especially important for Babel plugins that add, modify or remove imports or exports, but also for other transformations that add new variables as they can accidentally become global variables depending on the format. Therefore it is recommended that for formats other than esm
or cjs
, you set Rollup to use the esm
output format and let Babel handle the transformation to another format, e.g. via
presets: [['@babel/preset-env', { modules: 'umd' }], ...]
to create a UMD/IIFE compatible output. If you want to use getBabelOutputPlugin(...)
with other formats, you need to specify allowAllFormats: true
as plugin option:
rollup.rollup({...})
.then(bundle => bundle.generate({
format: 'iife',
plugins: [getBabelOutputPlugin({
allowAllFormats: true,
// ...
})]
}))
By default, helpers e.g. when transpiling classes will be inserted at the top of each chunk. In contrast to when applying this plugin on the input files, helpers will not be deduplicated across chunks.
Alternatively, you can use imported runtime helpers by adding the @babel/transform-runtime
plugin. This will make @babel/runtime
an external dependency of your project, see @babel/plugin-transform-runtime for details.
Note that this will only work for esm
and cjs
formats, and you need to make sure to set the useESModules
option of @babel/plugin-transform-runtime
to true
if you create ESM output:
rollup.rollup({...})
.then(bundle => bundle.generate({
format: 'esm',
plugins: [getBabelOutputPlugin({
presets: ['@babel/preset-env'],
plugins: [['@babel/plugin-transform-runtime', { useESModules: true }]]
})]
}))
// input
export default class Foo {}
// output
import _classCallCheck from '@babel/runtime/helpers/esm/classCallCheck';
var Foo = function Foo() {
_classCallCheck(this, Foo);
};
export default Foo;
And for CommonJS:
rollup.rollup({...})
.then(bundle => bundle.generate({
format: 'cjs',
plugins: [getBabelOutputPlugin({
presets: ['@babel/preset-env'],
plugins: [['@babel/plugin-transform-runtime', { useESModules: false }]]
})]
}))
// input
export default class Foo {}
// output
('use strict');
var _classCallCheck = require('@babel/runtime/helpers/classCallCheck');
var Foo = function Foo() {
_classCallCheck(this, Foo);
};
module.exports = Foo;
Another option is to use @babel/plugin-external-helpers
, which will reference the global babelHelpers
object. It is your responsibility to make sure this global variable exists.
@rollup/plugin-babel
exposes a plugin-builder utility that allows users to add custom handling of Babel's configuration for each file that it processes.
createBabelInputPluginFactory
accepts a callback that will be called with the loader's instance of babel
so that tooling can ensure that it using exactly the same @babel/core
instance as the loader itself.
It's main purpose is to allow other tools for configuration of transpilation without forcing people to add extra configuration but still allow for using their own babelrc / babel config files.
import { createBabelInputPluginFactory } from '@rollup/plugin-babel';
export default createBabelInputPluginFactory((babelCore) => {
function myPlugin() {
return {
visitor: {}
};
}
return {
// Passed the plugin options.
options({ opt1, opt2, ...pluginOptions }) {
return {
// Pull out any custom options that the plugin might have.
customOptions: { opt1, opt2 },
// Pass the options back with the two custom options removed.
pluginOptions
};
},
config(cfg /* Passed Babel's 'PartialConfig' object. */, { code, customOptions }) {
if (cfg.hasFilesystemConfig()) {
// Use the normal config
return cfg.options;
}
return {
...cfg.options,
plugins: [
...(cfg.options.plugins || []),
// Include a custom plugin in the options.
myPlugin
]
};
},
result(result, { code, customOptions, config, transformOptions }) {
return {
...result,
code: result.code + '\n// Generated by some custom plugin'
};
}
};
});
Author:
Source Code: https://github.com/rollup/plugins/
License:
#vite #rollup #typescript #javascript
1657008000
@rollup/plugin-dynamic-import-vars
🍣 A rollup plugin to support variables in dynamic imports in Rollup.
function importLocale(locale) {
return import(`./locales/${locale}.js`);
}
This plugin requires an LTS Node version (v10.0.0+) and Rollup v1.20.0+.
Using npm:
npm install @rollup/plugin-dynamic-import-vars --save-dev
Create a rollup.config.js
configuration file and import the plugin:
import dynamicImportVars from '@rollup/plugin-dynamic-import-vars';
export default {
plugins: [
dynamicImportVars({
// options
})
]
};
include
Type: String
| Array[...String]
Default: []
Files to include in this plugin (default all).
exclude
Type: String
| Array[...String]
Default: []
Files to exclude in this plugin (default none).
warnOnError
Type: Boolean
Default: false
By default, the plugin quits the build process when it encounters an error. If you set this option to true, it will throw a warning instead and leave the code untouched.
When a dynamic import contains a concatenated string, the variables of the string are replaced with a glob pattern. This glob pattern is evaluated during the build, and any files found are added to the rollup bundle. At runtime, the correct import is returned for the full concatenated string.
Some example patterns and the glob they produce:
`./locales/${locale}.js` -> './locales/*.js'
`./${folder}/${name}.js` -> './*/*.js'
`./module-${name}.js` -> './module-*.js'
`./modules-${name}/index.js` -> './modules-*/index.js'
'./locales/' + locale + '.js' -> './locales/*.js'
'./locales/' + locale + foo + bar '.js' -> './locales/*.js'
'./locales/' + `${locale}.js` -> './locales/*.js'
'./locales/' + `${foo + bar}.js` -> './locales/*.js'
'./locales/'.concat(locale, '.js') -> './locales/*.js'
'./'.concat(folder, '/').concat(name, '.js') -> './*/*.js'
Code that looks like this:
function importLocale(locale) {
return import(`./locales/${locale}.js`);
}
Is turned into:
function __variableDynamicImportRuntime__(path) {
switch (path) {
case './locales/en-GB.js':
return import('./locales/en-GB.js');
case './locales/en-US.js':
return import('./locales/en-US.js');
case './locales/nl-NL.js':
return import('./locales/nl-NL.js');
default:
return new Promise(function (resolve, reject) {
queueMicrotask(reject.bind(null, new Error('Unknown variable dynamic import: ' + path)));
});
}
}
function importLocale(locale) {
return __variableDynamicImportRuntime__(`./locales/${locale}.js`);
}
To know what to inject in the rollup bundle, we have to be able to do some static analysis on the code and make some assumptions about the possible imports. For example, if you use just a variable you could in theory import anything from your entire file system.
function importModule(path) {
// who knows what will be imported here?
return import(path);
}
To help static analysis, and to avoid possible foot guns, we are limited to a couple of rules:
./
or ../
.All imports must start relative to the importing file. The import should not start with a variable, an absolute path or a bare import:
// Not allowed
import(bar);
import(`${bar}.js`);
import(`/foo/${bar}.js`);
import(`some-library/${bar}.js`);
A folder may contain files you don't intend to import. We, therefore, require imports to end with a file extension in the static parts of the import.
// Not allowed
import(`./foo/${bar}`);
// allowed
import(`./foo/${bar}.js`);
If you import your own directory you likely end up with files you did not intend to import, including your own module. It is therefore required to give a more specific filename pattern:
// not allowed
import(`./${foo}.js`);
// allowed
import(`./module-${foo}.js`);
When generating globs, each variable in the string is converted to a glob *
with a maximum of one star per directory depth. This avoids unintentionally adding files from many directories to your import.
In the example below this generates ./foo/*/*.js
and not ./foo/**/*.js
.
import(`./foo/${x}${y}/${z}.js`);
Author: rollup
Source Code: https://github.com/rollup/plugins/
License:
#rollup #vite #typescript #javascript
1656993600
Simple analysis on rollup bundling, helping you to spot libaries bloating up your bundles.
/src/index.js:
codemirror - 446.92 KB (35.94%)
remarkable - 193.72 KB (15.58%)
app - 95.87 KB (7.71%)
autolinker - 81.64 KB (6.57%)
lodash.filter - 62.77 KB (5.05%)
...
or with way more details!
/src/index.js:
codemirror - 446.92 KB (35.94%)
lib\codemirror.js - 347.8 KB (77.82%)
mode\javascript\javascript.js - 27.78 KB (6.22%)
mode\markdown\markdown.js - 25.54 KB (5.72%)
mode\meta.js - 14.44 KB (3.23%)
mode\xml\xml.js - 12.52 KB (2.80%)
addon\edit\closebrackets.js - 7.04 KB (1.58%)
addon\edit\matchbrackets.js - 5.39 KB (1.21%)
addon\comment\continuecomment.js - 3.59 KB (0.80%)
addon\selection\active-line.js - 2.82 KB (0.63%)
remarkable - 193.72 KB (15.58%)
lib\common\entities.js - 46.44 KB (23.97%)
lib\rules.js - 10.2 KB (5.26%)
lib\rules_block\list.js - 6.65 KB (3.43%)
lib\ruler.js - 5.44 KB (2.81%)
lib\rules_block\deflist.js - 5.22 KB (2.69%)
...
$ npm i rollup-plugin-sizes -D
Add to your rollup build as the last plugin via JS API or Config file.
var rollup = require("rollup"),
buble = require("rollup-plugin-buble"),
sizes = require("rollup-plugin-sizes");
rollup.rollup({
entry : "src/main.js",
plugins : [
buble(),
sizes()
]
}).then(function(bundle) {
...
});
import buble from 'rollup-plugin-buble';
import sizes from 'rollup-plugin-sizes';
export default {
...
plugins : [
buble(),
sizes()
]
};
details
- Set to true to enable file-by-file breakdowns of space usage.
report
- Customize reporting. See source code for the default reporter.
Author: tivac
Source code: https://github.com/tivac/rollup-plugin-sizes\
License: MIT license
#javascript #rollup
1656982800
npm install --save-dev rollup-plugin-serve
yarn add rollup-plugin-serve
// rollup.config.js
import serve from 'rollup-plugin-serve'
export default {
input: 'src/main.js',
output: {
file: 'dist/bundle.js',
format: ...
},
plugins: [
serve('dist')
]
}
By default it serves the current project folder. Change it by passing a string:
serve('public') // will be used as contentBase
// Default options
serve({
// Launch in browser (default: false)
open: true,
// Page to navigate to when opening the browser.
// Will not do anything if open=false.
// Remember to start with a slash.
openPage: '/different/page',
// Show server address in console (default: true)
verbose: false,
// Folder to serve files from
contentBase: '',
// Multiple folders to serve from
contentBase: ['dist', 'static'],
// Set to true to return index.html (200) instead of error page (404)
historyApiFallback: false,
// Path to fallback page
historyApiFallback: '/200.html',
// Options used in setting up server
host: 'localhost',
port: 10001,
// By default server will be served over HTTP (https: false). It can optionally be served over HTTPS
https: {
key: fs.readFileSync('/path/to/server.key'),
cert: fs.readFileSync('/path/to/server.crt'),
ca: fs.readFileSync('/path/to/ca.pem')
},
//set headers
headers: {
'Access-Control-Allow-Origin': '*',
foo: 'bar'
},
// set custom mime types, usage https://github.com/broofa/mime#mimedefinetypemap-force--false
mimeTypes: {
'application/javascript': ['js_commonjs-proxy']
}
// execute function after server has begun listening
onListening: function (server) {
const address = server.address()
const host = address.address === '::' ? 'localhost' : address.address
// by using a bound function, we can access options as `this`
const protocol = this.https ? 'https' : 'http'
console.log(`Server listening at ${protocol}://${host}:${address.port}/`)
}
})
Please see CHANGELOG for more information what has changed recently.
Contributions and feedback are very welcome.
This project aims to stay lean and close to standards. New features should encourage to stick to standards. Options that match the behaviour of webpack-dev-server are always ok.
To get it running:
npm install
npm run build
Author: thgh
Source code: https://github.com/thgh/rollup-plugin-serve
License: MIT license
#rollup #javascript
1656950400
📟 Displays rollup errors as system notifications.
npm install --save-dev rollup-plugin-notify
Pretty simple, no settings, no options, just import the notify()
function and add it to your rollup config file. Then you can start rollup in an endless watch mode rollup -c -w
and minimize the terminal because all incoming errors will be caught and shown as notifications!
Rollup v0.60.0 or higher is required
// rollup.config.js
import notify from 'rollup-plugin-notify';
export default {
// ...
plugins: [
notify()
]
};
Check out ./example
folder for demo app.
We try to fit as much of useful information (file, code snippet, position) as possible in the little space of 4-line notification. But it is hard to test this module for all the plugins out there, each of which can and will throw different errors of various shapes.
Basic Rollup's parse errors and Babel plugin work great out of the box. If you feel like rollup-plugin-notify
could display better errors for your favorite Rollup plugin, let us know, or better yet, hit us up with a PR.
Contributions, help and feedback is welcome. Underlying node-notifier module is troublesome and difficult to test across platforms so testing and troubleshooting on other systems would be appreciated.
Author: MikeKovarik
Source code: https://github.com/MikeKovarik/rollup-plugin-notify
License: MIT license
#rollup #javascript
1654323025
Serverless Externals Plugin
Only include listed node_modules
and their dependencies in Serverless.
This plugin helps Serverless package only external modules, and Rollup bundle all the other modules.
npm install serverless-externals-plugin
or
yarn add serverless-externals-plugin
serverless.yml
:
plugins:
- serverless-externals-plugin
package:
individually: true
functions:
handler:
handler: dist/bundle.handler
externals:
report: dist/node-externals-report.json
package:
patterns:
- "!./**"
- ./dist/bundle.js
rollup.config.js
:
import { rollupPlugin as externals } from "serverless-externals-plugin";
export default {
...
output: { file: "dist/bundle.js", format: "cjs" },
treeshake: {
moduleSideEffects: "no-external",
},
plugins: [
externals(__dirname, { modules: ["pkg3"] }),
commonjs(),
nodeResolve({ preferBuiltins: true, exportConditions: ["node"] }),
...
],
}
Externals Plugin interacts with both Serverless and with your bundler (Rollup).
Let's say you have two modules in your package.json
, pkg2
and pkg3
. pkg3
is a module with native binaries, so it can't be bundled.
root
+-- pkg3@2.0.0
+-- pkg2@0.0.1
+-- pkg3@1.0.0
Because pkg3
can't be bundled, both ./node_modules/pkg3
and ./node_modules/pkg2/node_modules/pkg3
should be included in the bundle. pkg2
can just be bundled, but should import pkg3
as follows: require('pkg2/node_modules/pkg3')
. It cannot just do require('pkg3')
because pkg3
has a different version than pkg2/node_modules/pkg3
.
In the Serverless package, only ./node_modules/pkg3/**
and ./node_modules/pkg2/node_modules/pkg3/**
should be included, all the other contents of node_modules
are already bundled.
Externals Plugin provides a Serverless plugin and a Rollup plugin to support this.
There are other reasons modules can't be bundled. For example, readable-stream
and sshpk
cannot be bundled due to circular dependency errors.
In rollup.config.js
:
output: { file: "dist/bundle.js", format: "cjs" },
plugins: [
externals(__dirname, { modules: ["aws-sdk"] }),
...
]
This will generate a file called node-externals-report.json
next to bundle.js
, with the module paths that should be packaged.
It can then be included in serverless.yml
:
custom:
externals:
report: dist/node-externals-report.json
The configuration object has these options:
modules: string[]
: a list of module names that should be kept external (default []
)report?: boolean | string
: whether to generate a report or report path (default ${distPath}/node-externals-report.json
)packaging.exclude?: string[]
: modules which shouldn't be packaged by Serverless (e.g. ['aws-sdk']
, default []
)packaging.forceIncludeModuleRoots?: string[]
: module roots that should always be packaged (e.g. ['node_modules/pg']
, default []
)file?: string
: path to a different configuration objectIt's also possible to filter on module versions. (e.g. uuid@<8
). This uses a semver range.
Externals Plugin uses Arborist by NPM to analyze the node_modules
tree (using loadActual()
).
Using the Externals configuration (a list of modules you want to keep external), the Plugin will then build a list of all dependencies that should be kept external. This list will contain the modules in the configuration and all the (non-dev) dependencies, recursively.
In the example, the list will contain both pkg2/node_modules/pkg3
and pkg3
.
The Rollup Plugin will then generate a report (e.g. node-externals-report.json
) which contains the modules that are actually imported. This file is then used by the Serverless Plugin to generate a list of include patterns.
If you mark both aws-sdk
and sshpk
as external, but you don't use sshpk
, the generated report will look as follows:
node-externals-report.json
:
{
"isReport": true,
"importedModuleRoots": [
"node_modules/aws-sdk"
],
"config": {
"modules": [
"aws-sdk",
"sshpk"
]
}
}
Serverless can therefore ignore sshpk
and all its dependencies, making the bundle even smaller.
The report is generated by analyzing the files Rollup emits (including dynamic imports).
A fully configured rollup.config.js
could look as follows:
import { rollupPlugin as externals } from "serverless-externals-plugin";
import commonjs from "@rollup/plugin-commonjs";
import nodeResolve from "@rollup/plugin-node-resolve";
/** @type {import('rollup').RollupOptions} */
const config = {
input: "index.js",
output: {
file: "bundle.js",
format: "cjs",
exports: "default",
},
treeshake: {
moduleSideEffects: "no-external",
},
plugins: [
externals(__dirname, { modules: ["aws-sdk"] }),
commonjs(),
nodeResolve({ preferBuiltins: true, exportConditions: ["node"] }),
],
};
export default config;
Make sure externals
comes before @rollup/plugin-commonjs
and @rollup/plugin-node-resolve
.
Make sure moduleSideEffects: "no-external"
is set. By default, Rollup includes all external modules that appear in the code because they might contain side effects, even if they can be treeshaken. By setting this option Rollup will assume external modules have no side effects.
("no-external"
is equivalent to (id, external) => !external
)
Preferably, also set exportConditions: ["node"]
as an option in the node-resolve plugin. This ensures that Rollup uses Node's resolution algorithm, so that packages like uuid
can be bundled.
The Rollup plugin provides a resolveId
function to Rollup. For every import (e.g. require('pkg3')
) in your source code, Rollup will ask the Externals Plugin whether the import is external, and where to find it.
The Plugin will look for the import in the Arborist graph, and if it's declared as being external it will return the full path to the module that's being imported (e.g. pkg2/node_modules/pkg3
).
node-externals.json
It's also possible to add the externals config to a file, called node-externals.json
.
In node-externals.json
:
{
"modules": [
"pkg3"
]
}
In rollup.config.js
:
plugins: [
externals(__dirname, { file: 'node-externals.json' }),
...
]
If your code or one of your Node modules does the following:
require("p" + "g");
// or
import("p" + "g");
Then this plugin will not be able to detect which modules should be packaged. You can force include them by using packaging.forceIncludeModuleRoots
:
...
packaging: {
forceIncludeModuleRoots: ["node_modules/pg"]
}
The plugin will then treat node_modules/pg
as if it was imported directly in the bundle. It will also include all the dependencies.
For example: in knex
, an SQL builder, pg
is a peer dependency. Inside knex
it is imported as follows:
// knex/lib/index.js
const resolvedClientName = resolveClientNameWithAliases(clientName);
Dialect = require(`./dialects/${resolvedClientName}/index.js`);
// knex/lib/dialects/postgres/index.js
require('pg');
If you're using knex
, you'd have to force include node_modules/pg
. If you want to bundle knex
, you would also have to enable ignoreDynamicImports
in your rollup.config.js
:
commonjs({ ignoreDynamicRequires: true }),
This will make sure the require
call is not changed.
Another solution is to add knex
to the list of externals. In that case the whole node_modules/knex
folder will be uploaded, and none of its code will be transformed.
It's unlikely, but if you have external modules with side effects (like polyfills), make sure to configure Rollup properly.
NOTE: This only applies to external modules. You should probably bundle your polyfills.
import "some-external-module"; // this doesn't work, Rollup will treeshake it away
As Rollup will remove external modules with side effects, make sure to add something like this to the Rollup config:
treeshake: {
moduleSideEffects: (id, external) => !id.test(/some-external-module/) || !external
}
node_modules
supportedThis plugin doesn't have support for analyzing multiple node_modules
folders. If you have more node_modules
folders on your NODE_PATH
(e.g. from a Lambda layer), you can still use the external
field of Rollup.
aws-sdk
excludedAs the aws-sdk
node module is included by default in Lambdas, you can add packaging.exclude: ["aws-sdk"]
to exclude it from the Serverless package. Note that this is not recommended because of possible version differences. (Check the aws-sdk
version included in runtimes here)
When listing modules as external, all their subdependencies will also be marked as external.
For example:
// rollup.config.js
plugins: [
externals(__dirname, { modules: ["botkit"] }),
...
]
// lambda.js
const express = require('express');
const serverlessHttp = require('serverless-http');
const app = express();
module.exports.handler = serverlessHttp(app);
In the resulting bundle, express
will not be bundled. This is because botkit
also depends on express
, and is therefore marked as external. botkit
itself and the rest of its subdependencies will be filtered out of the modules to be uploaded.
It is therefore recommended to limit the length of the modules array to only the necessary. In that way you can achieve the smallest bundles.
.node
files), and bundling the restI wanted to include Cheerio/JSDom and AWS SDK in a Typescript project, but neither could be bundled because of obscure errors, so they needed to be external. To reduce package size, I didn't want to make every module external. Manually looking up a module and adding its dependencies to rollup.config.js
and serverless.yml
is simply too much work. This plugin makes this much easier.
Some Serverless-handling code was taken from Serverless Jetpack. Also inspired by Serverless Plugin Include Dependencies and Webpack Node Externals
Author: Bubblydoo
Source Code: https://github.com/bubblydoo/serverless-externals-plugin
License: MIT license
1654224300
コードベースのクリーンアップは、信頼できるコードを作成するための基本的なステップです。同じコードを異なる技術プラットフォーム用にコンパイルする必要がある場合、このプロセスは特に複雑になる可能性があります。現代のウェブ開発はこの良い例です。
現在、単純なAPIを提供する単一のサーバーがある場合でも、数十の異なる小さな機能を処理できる可能性があります。これはすぐに混乱する可能性がありますが、幸いなことに、私たちの生活を楽にするプロセスとツールがあります。
この記事では、ロールアップの使用について説明します。Rollupは、新しいライブラリのすべての機能を1つのバンドルにまとめるプロセスを支援するように設計されたモジュールバンドラーです。
Rollupの背後にある考え方は、Rollupがバンドルを準備し、それをバックエンドアプリケーションのライブラリであろうとブラウザのモジュールであろうと、最も効率的な方法でラップする間、関数に集中できるようにすることで開発を簡素化することです。
Rollupの公式ドキュメントによると、「RollupはJavaScriptのモジュールバンドラーであり、小さなコードをライブラリやアプリケーションなど、より大きく複雑なものにコンパイルします。」
これは、新しいライブラリを設計する際の興味深いスタンスであり、2000年代初頭に一般的だった長い設計セッションから一歩離れています。当時は、関数とオブジェクトを設計してから、それらを1つのモジュールにバンドルしていました。これにより、インターフェイスが均質化され、実行する場所またはプラットフォームに簡単に統合できるようになります。
Rollupを特別なものにしているのは、ファイルを小さく保つ機能です。これは2つの方法で実現されます。1つは、RollupがESモジュール(CommonJSモジュールよりも効率的)に基づいているためです。2つ目は、生成されたバンドルから未使用のコードを削除するためです。
ロールアップの効率を証明するために、デモライブラリを使用します。課題(もちろん、私たちが勝つことになる)は、まったく同じコードベースを持ち、それをコンパイルしてTypeScriptとJavaScriptの両方で使用することです。
このデモで使用する小さなデモライブラリは、2つの関数セットを並べたものです。1つは文字列用で、もう1つは数値用です。ここにあるセットアップを保存するために、GitHubにリポジトリをアセンブルしました。
一般的な考え方は、と呼ばれるファイルにまったく同じ基本関数(/src
ディレクトリにあります)を収集することです。numbersManipulation.jsstringsManipulation.js
使用する予定の関数は、別のファイルに収集されますgameLibrary.js
。このファイルは、アプリケーションで使用する予定のファイルです。
アイデアは、ロールアップを使用して、さまざまなライブラリからの機能をバンドルする方法を模倣することです。
先に進む前に、プロジェクトをセットアップして実行し、すべてが正常に機能していることを確認しましょう。まず、実行してnpm i
(必要に応じて使用することもできyarn
ます)、すべてのパッケージをダウンロードします。
これで、を使用してテストスクリプトを実行する準備が整いましたnpm run test
。これにより、バンドルがコンパイルされ、JavaScript(appJS.js
)バージョンとTypeScriptバージョン(appTS.ts
)の両方のバージョンのアプリケーションが実行されます。
以下に示す実行の出力は、2つのスクリプトとを実行するだけappJS.js
ですappTS.ts
。どちらのスクリプトも同じコードベースであるライブラリを使用しており、Rollupによって生成されるファイル/src/gameLibrary.js
に貢献します。bundle.js
/src
ディレクトリに含まれるリポジトリを見てみましょう。前述のように、アプリケーションで使用する予定のライブラリと、に含まれるrollup -c
スクリプトで実行されるコマンドは、を読み取り、ディレクトリ内のバンドルをアセンブルします。buildpackage.jsonrollup.configuration.js/dist
そのようなディレクトリの内容を考えてみましょう:
最初のファイルは、d.ts
TypeScriptの既存のJavaScriptコードベースの形状を記述した宣言ファイルです。
2番目のファイル、bundle.js
は最も興味深いファイルです。これは、Rollupを使用して作成したバンドルです。
3番目のファイルは.map
、バンドルを縮小したい場合に備えて、元のソースを追跡するために使用されます。
次に、ツリーシェイクプロセスがどのように機能するかについて簡単に説明します。
木の揺れはロールアップの重要な機能です。ツリーシェイクを使用すると、最小限の機能を含むバンドルを作成して、コードをより軽量かつ高速に保つことができます。
上の図では、コードの静的分析によって差し引かれる関数呼び出しツリーを見ることができます。関数が列挙され、使用するコードとの関連性が追跡されます。
この分析の後、関連する機能を収集して並置することにより、バンドルが組み立てられます。下の写真bundle.js
を除いて、すべての機能を含むファイルの結果を見ることができます。どのコードにもリンクされていなかったため、破棄されました。isOdd()isOdd()
比喩的に言えば、関数isOdd()
はどのブランチにも接続されていないため、依存関係のツリーを振ることによって機能が低下しました。
もう1つの興味深い詳細は、紺色のブロックに表示されます。これらは、バンドルで明示的にエクスポートされないが、関数によって使用されるconvert()
関数です。予想どおり、それらはbundle.js
にあり、水色のブロックは独自のファイルに明示的にエクスポートされています。
一般的に、ツリーシェイクは、プロジェクトに多数のサードパーティライブラリとフレームワークが必要な場合に効果的です。各ライブラリとフレームワークには、数十の関数とメソッドがあります。
バンドルは、を実行して作成されrollup.config.js
ます。これは、バンドルを作成するための入力ファイルとプラグインの観点から実行される操作について説明しています。
import dts from 'rollup-plugin-dts'
import esbuild from 'rollup-plugin-esbuild'
export default [
{
input: `src/gameLibrary.js`,
plugins: [esbuild()],
output: [
{
file: `dist/bundle.js`,
format: 'cjs',
sourcemap: true,
exports: 'default',
},
]
},
{
input: `src/gameLibrary.js`,
plugins: [dts()],
output: {
file: `dist/bundle.d.ts`,
format: 'es',
},
}
]
上記のコードでは、GitHubリポジトリから構成ファイルを確認できます。魔法がどこで起こるかを理解するためにこれについて話し合いましょう。
このファイルは、ロールアップに2つの操作を実行するように指示します。7行目でバンドルをアセンブルし、 19行目でプラグインを使用してファイルをesbuild plugin
生成します。両方の手順は、6行目と18行目に示されている同じ入力ファイルで実行されます。d.tsdts/src/gameLibrary.js
このファイル/src/gameLibrary.js
は、バンドル作成プロセスで「シェイク」される依存関係ツリーのルートです。
プラグインは、esbuild
11行目に示されているCommonJSバンドルと、.map
12行目の値によって要求されたソースマップ(前のセクションで見たファイル)を生成します。ロールアップ構成ファイルの一般的な構造は注目に値します。それぞれが独自の構成を持つプラグインの配列をエクスポートします。
利用可能なプラグインの(そうではない)完全なリストについては、このリポジトリを確認できます。
この投稿では、Rollupフレームワークを使用して、関数のさまざまなライブラリからのコードを均質化する方法と、2つの異なる言語で使用するための効率的なバンドルを作成する方法を示しました。
私の意見では、ロールアップは、さまざまなコンテキスト、言語、またはプラットフォームで使用される可能性のあるすべてのプロジェクトに採用できます。これは、大小を問わず、必要になる可能性のある微調整の表面を提供し、実際に生成しているコードのサイズを監視できるようにします。
このストーリーは、もともとhttps://blog.logrocket.com/using-rollup-package-library-typescript-javascript/で公開されました
1654180867
Limpiar su base de código es un paso fundamental para escribir código confiable. Este proceso puede ser particularmente complejo cuando se debe compilar el mismo código para diferentes plataformas tecnológicas. El desarrollo web moderno es un gran ejemplo de esto.
Hoy en día, incluso cuando tiene un solo servidor que proporciona una API simple, es probable que aún maneje decenas de funcionalidades diferentes más pequeñas. Esto puede volverse confuso bastante rápido, pero afortunadamente existen procesos y herramientas para hacernos la vida más fácil.
En este artículo, discutiremos el uso de Rollup. Rollup es un paquete de módulos diseñado para ayudar en el proceso de recopilar todas las funcionalidades de su nueva biblioteca en un solo paquete.
La idea detrás de Rollup es simplificar el desarrollo permitiéndole concentrarse en la función mientras Rollup prepara el paquete y lo envuelve de la manera más eficiente, ya sea una biblioteca para una aplicación de back-end o un módulo para el navegador.
Según la documentación oficial de Rollup , "Rollup es un paquete de módulos para JavaScript que compila pequeñas piezas de código en algo más grande y complejo, como una biblioteca o una aplicación".
Esta es una postura interesante en el diseño de una nueva biblioteca y está a un paso de las largas sesiones de diseño que eran comunes a principios de la década de 2000. En ese entonces, diseñaría sus funciones y objetos y luego los agruparía en un solo módulo. Esto homogeneizaría las interfaces y permitiría que se integren fácilmente en el lugar o plataforma para ejecutarse.
Lo que hace que Rollup sea especial es su capacidad para mantener archivos pequeños. Lo logra de dos maneras: primero debido al hecho de que Rollup se basa en módulos ES (más eficientes que los módulos CommonJS); y segundo porque elimina el código no utilizado del paquete producido.
Para probar la eficiencia de Rollup, usaremos una biblioteca de demostración. El desafío (que ganaremos, por supuesto) es tener exactamente el mismo código base y luego compilarlo y usarlo con TypeScript y JavaScript.
La pequeña biblioteca de demostración que usaremos en esta demostración es una yuxtaposición de dos conjuntos de funciones, una para cadenas y otra para números. Reuní un repositorio en GitHub para ahorrarte algo de configuración, que se encuentra aquí .
La idea general es tener exactamente las mismas funciones básicas (las encontrará en el /src
directorio) recopiladas en los archivos llamados numbersManipulation.js
y stringsManipulation.js
.
Las funciones que pretendemos utilizar están recogidas en otro archivo, gameLibrary.js
. Este mismo archivo es el que pretendemos usar en nuestra aplicación.
La idea es imitar la forma en que se usa Rollup para agrupar funcionalidades provenientes de diferentes bibliotecas.
Antes de continuar, configuremos y ejecutemos el proyecto para comprobar que todo funciona bien. Primero, ejecuta npm i
(también puedes usar yarn
si lo prefieres) para descargar todos los paquetes.
Ahora, está listo para ejecutar el script de prueba con npm run test
, que compilará el paquete y ejecutará ambas versiones de las aplicaciones: la versión de JavaScript ( appJS.js
) y la versión de TypeScript ( appTS.ts
).
El resultado de la ejecución, que se ve a continuación, simplemente ejecutará los dos scripts appJS.js
y appTS.ts
. Ambos scripts usan el mismo código base, la biblioteca /src/gameLibrary.js
, que contribuirá al bundle.js
archivo producido por Rollup.
Veamos el repositorio que /src
contiene el directorio. Como se mencionó anteriormente, las bibliotecas que pretendemos usar en nuestras aplicaciones y el comando rollup -c
, ejecutado en el script build
contenido en el package.json
, leerá rollup.configuration.js
y ensamblará el paquete en el /dist
directorio.
Consideremos el contenido de dicho directorio:
El primer archivo es el d.ts
archivo de declaración que describe la forma de un código base de JavaScript existente para TypeScript.
El segundo archivo, bundle.js
, es el más interesante. Este es el paquete que creamos usando Rollup.
El tercer archivo es .map
y se usa para realizar un seguimiento de la fuente original, en caso de que queramos minimizar el paquete.
Ahora, discutiremos brevemente cómo funciona el proceso de sacudir árboles.
La sacudida de árboles es una característica importante en Rollup. El movimiento del árbol permite la creación de un paquete que incluye la funcionalidad mínima, manteniendo el código más ligero y rápido.
En la imagen de arriba, puede ver el árbol de llamadas a funciones que se deduce del análisis estático del código. Las funciones se enumeran y se rastrea su relevancia para el código que vamos a utilizar.
Después de este análisis, el paquete se ensambla reuniendo y yuxtaponiendo las funciones relevantes. Puede ver el resultado del bundle.js
archivo que contiene todas las funciones, excepto isOdd()
en la foto a continuación. isOdd()
fue descartado porque no estaba vinculado a ningún código.
Metafóricamente, la función isOdd()
se vino abajo sacudiendo el árbol de las dependencias porque no estaba adherido a ninguna rama.
Otro detalle interesante se muestra en los bloques de color azul oscuro. Estas son funciones que no se exportan explícitamente en el paquete, pero son utilizadas por la convert()
función. Como era de esperar, los encontrará en bundle.js
, y los bloques de color azul claro se exportarán explícitamente en sus propios archivos.
En términos generales, la sacudida de árboles es efectiva cuando su proyecto necesita muchas bibliotecas y marcos de trabajo de terceros que tienen docenas de funciones y métodos disponibles.
El paquete se crea ejecutando rollup.config.js
. Esto describe las operaciones que se realizarán en términos de archivos de entrada y complementos para crear el paquete.
import dts from 'rollup-plugin-dts'
import esbuild from 'rollup-plugin-esbuild'
export default [
{
input: `src/gameLibrary.js`,
plugins: [esbuild()],
output: [
{
file: `dist/bundle.js`,
format: 'cjs',
sourcemap: true,
exports: 'default',
},
]
},
{
input: `src/gameLibrary.js`,
plugins: [dts()],
output: {
file: `dist/bundle.d.ts`,
format: 'es',
},
}
]
En el código anterior, puede ver el archivo de configuración de mi repositorio de GitHub . Discutamos esto para entender dónde ocurre la magia.
El archivo le indica a Rollup que realice dos operaciones: ensamble el paquete usando la esbuild plugin
línea 7 y genere el d.ts
archivo usando el dts
complemento en la línea 19. Ambos pasos se realizarán en el mismo archivo de entrada /src/gameLibrary.js
, que se muestra en las líneas 6 y 18.
El archivo /src/gameLibrary.js
es la raíz del árbol de dependencias que se “sacudirá” en el proceso de creación del paquete.
El esbuild
complemento generará un paquete CommonJS, que se muestra en la línea 11, y también un mapa de origen (el .map
archivo que vimos en la sección anterior) según lo solicitado por el valor en la línea 12. Vale la pena señalar que la estructura general del archivo de configuración Rollup exporta una variedad de complementos, cada uno con sus propias configuraciones.
Puede consultar este repositorio para obtener una lista (no tan) completa de los complementos disponibles.
En esta publicación, demostramos cómo se puede usar el marco Rollup para homogeneizar el código proveniente de diferentes bibliotecas de funciones, así como también cómo producir un paquete eficiente para usar en dos idiomas diferentes.
En mi opinión, Rollup puede adoptarse para cualquier proyecto que pueda usarse en diferentes contextos, lenguajes o plataformas. Ofrece una superficie para los ajustes que pueda necesitar, ya sean grandes o pequeños, lo que le permite controlar el tamaño del código que realmente está produciendo.
Esta historia se publicó originalmente en https://blog.logrocket.com/using-rollup-package-library-typescript-javascript/
1653298560
Rollup
Rollup is a module bundler for JavaScript which compiles small pieces of code into something larger and more complex, such as a library or application. It uses the standardized ES module format for code, instead of previous idiosyncratic solutions such as CommonJS and AMD. ES modules let you freely and seamlessly combine the most useful individual functions from your favorite libraries. Rollup can optimize ES modules for faster native loading in modern browsers, or output a legacy module format allowing ES module workflows today.
Install with npm install --global rollup
. Rollup can be used either through a command line interface with an optional configuration file, or else through its JavaScript API. Run rollup --help
to see the available options and parameters. The starter project templates, rollup-starter-lib and rollup-starter-app, demonstrate common configuration options, and more detailed instructions are available throughout the user guide.
These commands assume the entry point to your application is named main.js, and that you'd like all imports compiled into a single file named bundle.js.
For browsers:
# compile to a <script> containing a self-executing function
rollup main.js --format iife --name "myBundle" --file bundle.js
For Node.js:
# compile to a CommonJS module
rollup main.js --format cjs --file bundle.js
For both browsers and Node.js:
# UMD format requires a bundle name
rollup main.js --format umd --name "myBundle" --file bundle.js
Developing software is usually easier if you break your project into smaller separate pieces, since that often removes unexpected interactions and dramatically reduces the complexity of the problems you'll need to solve, and simply writing smaller projects in the first place isn't necessarily the answer. Unfortunately, JavaScript has not historically included this capability as a core feature in the language.
This finally changed with ES modules support in JavaScript, which provides a syntax for importing and exporting functions and data so they can be shared between separate scripts. Most browsers and Node.js support ES modules. However, Node.js releases before 12.17 support ES modules only behind the --experimental-modules
flag, and older browsers like Internet Explorer do not support ES modules at all. Rollup allows you to write your code using ES modules, and run your application even in environments that do not support ES modules natively. For environments that support them, Rollup can output optimized ES modules; for environments that don't, Rollup can compile your code to other formats such as CommonJS modules, AMD modules, and IIFE-style scripts. This means that you get to write future-proof code, and you also get the tremendous benefits of...
In addition to enabling the use of ES modules, Rollup also statically analyzes and optimizes the code you are importing, and will exclude anything that isn't actually used. This allows you to build on top of existing tools and modules without adding extra dependencies or bloating the size of your project.
For example, with CommonJS, the entire tool or library must be imported.
// import the entire utils object with CommonJS
var utils = require('utils');
var query = 'Rollup';
// use the ajax method of the utils object
utils.ajax('https://api.example.com?search=' + query).then(handleResponse);
But with ES modules, instead of importing the whole utils
object, we can just import the one ajax
function we need:
// import the ajax function with an ES import statement
import { ajax } from 'utils';
var query = 'Rollup';
// call the ajax function
ajax('https://api.example.com?search=' + query).then(handleResponse);
Because Rollup includes the bare minimum, it results in lighter, faster, and less complicated libraries and applications. Since this approach is based on explicit import
and export
statements, it is vastly more effective than simply running an automated minifier to detect unused variables in the compiled output code.
Rollup can import existing CommonJS modules through a plugin.
To make sure your ES modules are immediately usable by tools that work with CommonJS such as Node.js and webpack, you can use Rollup to compile to UMD or CommonJS format, and then point to that compiled version with the main
property in your package.json
file. If your package.json
file also has a module
field, ES-module-aware tools like Rollup and webpack will import the ES module version directly.
Author: Rollup
Source Code: https://github.com/rollup/rollup
License: View license
1649165820
Ethereum, the most popular blockchain, has seen scaling issues for quite a long time now. With high gas fees due to congestion being the primary pain point. With the increasing cost to use the network, a plethora of scaling solutions have emerged. Today in this guide, we will go over some Ethereum scaling solutions and deep dive into rollups.
The main goal of all the Ethereum scaling solutions is to increase transaction speed and throughput without compromising the decentralized nature of the blockchain.
There are two main directions in which these scaling solutions are being explored:
On-chain Scaling
On-chain or Layer 1 scaling solutions refer to increasing the capacity of the core blockchain layer, usually by increasing the numbers of transactions that can be fit in a block. In Ethereum’s case, on-chain scaling would mean changing the Ethereum Mainnet protocol. Increasing the amount of data/transactions that can fit into an Ethereum block would also increase the hardware requirements to run a node, raising some critics around this scaling solution. It can limit the number of nodes and may affect decentralization. But, with Ethereum 2 comes a concept of Sharding; sharding is the process of splitting the Ethereum infrastructure/database. This will reduce the node load by dividing the work; it is a much broader concept.
Off-chain Scaling
Off-chain scaling solutions are meant to indirectly scale the primary layer one blockchain by adding more layers to process the transactions without using the actual core blockchain. Off-chain scaling uses the main layer of blockchain as a trust and arbitration layer. Off-chain scaling is often referred to as Layer 2 scaling as it adds another layer on top of the main Ethereum layer.
Layer 2 scaling is seen as an immediate resolution for Ethereum scalability as it maintains decentralization, which is the most valuable thing for the Ethereum community. Layer 2 solutions further require additional hardware or complex software to be built, so it takes longer for networks to feel its effects. Layer 2 solutions derive their security from Ethereum and can be custom built/run by an individual, organization, or community based on the use cases.
Following are types of solutions:
On one hand, we have layer 2 solutions such as channels that are fully secure by Ethereum security but work well for only a specific set of applications; sidechains, on the other hand, are usually EVM compatible and can scale general purpose applications, but the main drawback is that they do not rely on Ethereum for security but instead have their own consensus model.
Rollups try to bring the best of both of these worlds by creating a general-purpose solution while still having the security of Ethereum. Rollups settle the transactions outside of the main Ethereum network but post the transaction data back to the Ethereum network and still derive its security from the Ethereum protocol. Each rollup has its specific contracts deployed on the main Ethereum network. Rollups executes the transaction off the chain mainly on a rollup specific chain and then batch the transaction data, compresses it, and sends it to the main Etheruem chain; this reduces the load on the main Ethereum network of actually processing those transactions.
This helps in reducing the fees and blockchain adoption by increasing participation. Rollups also enable redeployment of all the existing Ethereum smart contracts to a rollup with little or no change.
Rollups look like an up-and-coming solution to the Ethereum scalability problem, but how can we make sure that the transaction data posted on Ethereum by rollups is valid? Different rollup types handle this differently. There are two types of rollups based on this; Optimistic Rollups and ZK-rollups.
Optimistic rollups:
Optimistic rollups, as the name suggests at first, assume that the transaction data submitted to the Ethereum network is correct and valid. Whenever there is an invalid transaction, there is a dispute resolution. A party submits a batch of transaction data to Ethereum, and whenever someone detects a fraudulent transaction, they can offer fraud proof against that transaction. Here both the parties, the one submitting the transaction data batch and the one submitting the fraud proof, have their ETH staked. This means that any misconduct from either party would result in loss of their ETH. Whenever a fraud proof is submitted, the suspicious transaction is executed again, this time on the main Ethereum network. To make sure the transaction is replayed with the exact state when it was originally performed on the rollup chain, a manager contract is created that replaces certain function calls with a state from the rollup.
ZK-rollups:
ZK-rollups or Zero-Knowledge rollups, unlike Optimistic rollups, don’t have any dispute resolution mechanism. It uses a clever piece of cryptography Zero-Knowledge proofs. In this model, every batch of transactions submitted to Ethereum includes a cryptographic proof called a SNARK ( Succinct Non-Interactive Argument of Knowledge ) verified by a contract that is deployed on the Ethereum main network. This contract maintains the state of all transfers on the rollups chain, and this state can be updated only with validity proof. This means that only the validity proof needs to be stored on the main Ethereum network instead of bulky transaction data, thus making zk-rollups quicker and cheaper comparatively.
Examples: Loopring, STARKWARE, zkSync.
If you made it here now, you know a little bit more about Ethereum scaling solutions and a lot more about rollups.
Link: https://www.quicknode.com/guides/infrastructure/introduction-to-ethereum-rollups
1648106340
Rollups are way to "roll up" a bunch of transactions into one single piece of data to save space and processing power on the blockchain. Right now, there are 2 main rollups: ZKsnarks and Optimistic rollups and you can watch our video to learn how both work, including the benefits and drawbacks.