GithubHelp home page GithubHelp logo

webpack-contrib's Introduction

@dojo/webpack-contrib

Build Status codecov npm version

This repository contains all of the custom Webpack plugins and loaders used by @dojo/cli-build-app and @dojo/cli-build-widget to facilitate building Dojo 2 applications and widgets, respectively.


Usage

To use @dojo/webpack-contrib in a single project, install the package:

npm install @dojo/webpack-contrib

Loaders

css-module-decorator-loader

A webpack loader that injects CSS module path information into the generated modules, making it easier for @dojo/widget-core to resolve theme styles. This loader takes no options.

css-module-dts-loader

A webpack loader that generates .d.ts files for CSS modules. This is used by both @dojo/cli-build-app and @dojo/cli-build-widget to provide in-widget type checks for CSS classes.

The loader expects the following options:

Property Type Optional Description
type 'ts' or 'css' No The type of file being processed. If ts, the loader assumes the resource is a TypeScript file and will parse it for CSS module imports. If any CSS modules are found, then the loader generates a d.ts file for each. If css is specified, then the loader assumes the resource is a CSS module and generates the .d.ts file for it.
instanceName string Yes An optional instance name for the TypeScript compiler created by the ts-loader. This option is valid only when the type is ts, and ensures the same compiler instance is used across all files.

Example:

const webpackConfig = {
	loaders: [
		// Generate `.d.ts` files for all CSS modules imported in TypeScript files beneath
		// the `src/` directory
		{
			include: './src/',
			test: /\.ts$/,
			enforce: 'pre',
			loader: '@dojo/webpack-contrib/css-module-dts-loader?type=ts&instanceName=APP_NAME'
		},
		// Generate `.d.ts` files for all CSS modules beneath the `src/` directory
		{
			include: './src/',
			test: /\.m\.css$/,
			enforce: 'pre',
			loader: '@dojo/webpack-contrib/css-module-dts-loader?type=css'
		}
	]
};

promise-loader

A webpack loader that returns a promise from require() calls that resolves to the requested resource. It also removes absolute paths from source maps, ensuring consistent builds across environments. It is used by @dojo/cli-build-app for lazily loading bundles.

This loader expects two parameters, one required and one optional:

  1. The required promise implementation (e.g., bluebird). If there already exists a global Promise constructor, then global should be specified.
  2. An optional chunk name.

Example:

const resourcePromise = require('@dojo/webpack-contrib/promise-loader?global,resource-chunk-name!./path/to/resource');
resourcePromise.then((resource) => {
	// resource is available
})

static-build-loader

A webpack loader which allows code to be statically optimized for a particular context at bundling time. This loader acts on JavaScript. Some examples show the TypeScript source, but the loader will only work if acting on the compiled output.

Features

The loader examines code, looking for usages of @dojo/has, @dojo/has.exists, @dojo/has.add, or has pragmas to optimize. It does this by parsing the AST structure of the code, and modifying it when appropriate.

The loader takes three options:

  • features: A map of static features or a feature or list of features that resolve to a similar static map based on the functionality provided by the specified targets. Each key in the map is the name of the feature and the value is true if the feature is present in that context, otherwise false.
  • isRunningInNode: An optional boolean parameter. If set to false this indicates that the loader will not be running in an environment with a Node-like require.
  • staticOnly: A list of feature keys that should not have has.add calls modified. In most cases, this option will not be needed. It is provided for cases where a module will sometimes be parsed and have its flags resolved statically if so, but may also not be parsed and may require a different runtime value for the flag.

For example in a webpack configuration, the map of features would look like this:

{
    use: [
        {
            loader: '@dojo/webpack-contrib/static-build-loader',
            options: {
                features: {
                    'foo': true,
                    'bar': false
                },
                staticOnly: ['bar']
            }
        }
    ]
};

This asserts feature foo is true and feature bar is false. Alternatively a list of features can be provided that will be resolved to the appropriate map

{
	use: [
		{
			loader: '@dojo/webpack-contrib/static-build-loader',
			options: {
				features: [ 'firefox', 'chrome' ]
			}
		}
	]
}

Available features

When specifying a static map, any values can be used. When passing a string or list of strings, the following values are supported. Each value corresponds to the set of known features that the environment supports. If multiple features are specified, the intersection of available features will be returned.

  • android
  • chrome
  • edge
  • firefox
  • ie11
  • ios
  • node
  • node8
  • safari

In either case, the resulting map is then used in the features below.

Dead Code Removal

The loader assumes that the @dojo/has API is being used in modules that are being compiled into a webpack bundle and attempts to rewrite calls to the has() API when it can see it has a statically asserted flag for that feature.

The loader detects structures like the following in transpiled TypeScript modules:

import has from './has';

if (has('foo')) {
    console.log('has foo');
}
else {
    console.log('doesn\'t have foo');
}

const bar = has('bar') ? 'has bar' : 'doesn\'t have bar';

And will rewrite the code (given the static feature set above), like:

import has from './has';

if (true) {
    console.log('has foo');
}
else {
    console.log('doesn\'t have foo');
}

const bar = false ? 'has bar' : 'doesn\'t have bar';

When this is minified via Uglify via webpack, Uglify looks for structures that can be optimised and would re-write it further to something like:

import has from './has';

console.log('has foo');

const bar = 'doesn\'t have bar';

Any features which are not statically asserted, are not re-written. This allows the code to determine at run-time if the feature is present.

Elided Imports

The loader looks for has pragmas, which are strings that contain a call to has for a specific feature, and removes the next import found in the code. For example, given the above feature set, which has foo = true and bar = false, the imports of 'a' and 'b' would be removed but 'c' and 'd' would remain.

"has('foo')";
const statementBeforeImport = 3;
// This is the next import so it will be removed despite
// the conetnet between it and the pragma
import 'a';
// The pragma can be negated to remove the import if the condition
// is known to be false
'!has("bar")';
import 'b';
'!has("foo")';
// This import will not be removed because `foo` is not false
import 'c';

'has("baz")';
// This import will not be removed because the value of `has('baz')`
// is now known statically
import 'd';

Plugins

build-time-render

A webpack plugin that generates a bundle's HTML and inlines critical CSS at build time. This is similar to server-side rendering, but does not require a dedicated server to manage it.

The plugin takes an options object with the following properties:

Property Type Optional Description
entries string[] No The entry scripts to include in the generated HTML file
paths string[] or { path: string; match: string[] } Yes An array of paths that will be matched against the URL hash, rendering the HTML associated with any matched path. If the path is a string, then it will be compared directly to window.location.hash. If the path is an object, then it should include a path string that will be used to resolve the HTML, and match string array that represents multiple paths that should trigger the path to be rendered. Defaults to an empty array ('[]').
root string Yes The ID for the root HTML element. If falsy, then no HTML will be generated. Defaults to an emtpy string ('').
useManifest boolean Yes Determines whether a manifest file should be used to resolve the entries. Defaults to false.
static boolean Yes Removes js and css scripts tags from generated index.html files for truly static sites. Not compatible with hash routing. Defaults to false.

Usage

Beyond the plugin itself, it is also beneficial to include the @dojo/webpack-contrib/build-time-render/hasBuildTimeRender module in the entry config option. This file adds the has('build-item-render') flag that applications can use to determine whether the app has been pre-rendered.

The following example will generate an HTML file containing <script> tags for the specified entries, along with the critical CSS and HTML for the root, #account, and #help paths. Both the #faq and #contact hashes will display the #help HTML.

import BuildTimeRender from '@dojo/webpack-contrib/build-time-render/BuildTimeRender';

const entry = {
	main: {
		'@dojo/webpack-contrib/build-time-render/hasBuildTimeRender',
		'./src/main.ts',
		'./src/main.css'
	}
};

const webpackConfig = {
	entry,
	plugins: [
		new BuildTimeRender({
			entries: Object.keys(entry),
			paths: [
				'#account',
				{ path: '#help', match: [ 'faq', 'contact' ] }
			],
			root: 'app',
			useManifest: true
		})
	]
};

css-module-plugin

A webpack plugin that converts .m.css import paths to .m.css.js. This is used in conjunction with the css-module-dts-loader loader to ensure that webpack can probably resolve CSS module paths.

If the requested resource uses a relative path, then its path will be resolved relative to the requesting module. Otherwise, the resource will be loaded from ${basePath}node_modules (see below for the definition of basePath):

// Path is relative to the current module
import * as localCss from './path/to/local-styles.m.css';

// Imported from node_modules
import * as externalCss from 'some-mid/styles.m.css';

The plugin constructor accepts a single argument:

Property Type Optional Description
basePath string No The root path from which to resolve CSS modules

emit-all-plugin

Webpack is a bundler, which does not work well when build libraries. Rather than create separate toolchains for building libraries and applications/custom elements, the existing build tooling can be used to emit library files by way of the emitAllFactory that produces both a plugin instance and a custom TypeScript transformer. The transformer is needed to ensure that any .d.ts dependencies are correctly emitted, while the plugin ensures assets are emitted as individual files instead of as a generated bundle:

import { emitAllFactory } from '@dojo/webpack-contrib/emit-all-plugin/EmitAllPlugin';

// See list of available options below
const emitAll = emitAllFactory(options);

const config = {
	// ...
	plugins: [
		...,
		emitAll.plugin
	],
	module: {
		rules: [
			// ...
			{
				test: /.*\.ts(x)?$/,
				use: [
					{
						loader: 'ts-loader',
						options: {
							// ...
							getCustomTransformers(program) {
								return {
									before: [
										emitAll.transformer()
									]
								};
							}
						}
					}
				]
			}
		]
	}
}

At the very least CSS and transpiled TS files will be emitted, but additional assets can be included with a filter.

The factory takes an options object with the following properties:

Property Type Optional Description
basePath string Yes The base path for the project's source files. Only files within this directory are emitted. Defaults to the project's src/ directory.
inlineSourceMaps boolean Yes Specifies whether sources maps should be inlined with the output source files or emitted separately. Defaults to false.
legacy boolean Yes Specifies whether TypeScript files should be transpiled to ES modules or to legacy JavaScript. Defaults to false.
assetFilter Function Yes Used to filter assets to only those that should be included in the output. Accepts the asset file path and the asset object. If the function returns true, the asset will be emitted; otherwise it will be excluded.

external-loader-plugin

External libraries that cannot be loaded normally via webpack can be included in a webpack build using this plugin.

The plugin takes an options object with the following properties:

Property Type Optional Description
dependencies ExternalDep[] No External dependencies to load. Described in more detail below
hash boolean Yes Whether to use the build's hash to cache bust injected dependencies
outputPath string Yes Where to copy dependencies to; defaults to "externals"
pathPrefix string Yes Used to change the directory where files are placed(e.g. placing files in _build for testing)

All external dependencies specified in the dependencies options will be placed in ${pathPrefix}/${outputPath}. Each ExternalDep in the dependencies array specifies one external dependency. Each can be a string, indicating a path that should be delegated to the configured loader, or an object with the following properties:

Property Type optional Description
from string false A path relative to the root of the project specifying the dependency location to copy into the build application.
to string true A path that replaces from as the location to copy this dependency to. By default, dependencies will be copied to ${externalsOutputPath}/${to} or ${externalsOutputPath}/${from} if to is not specified. If the path includes . characters, it must end in a forward slash to be treated as a directory
name string true Indicates that this path, and any children of this path, should be loaded via the external loader
inject string, string[], or boolean true This property indicates that this dependency defines, or includes, scripts or stylesheets that should be loaded on the page. If inject is set to true, then the file at the location specified by to or from will be loaded on the page. If this dependency is a folder, then inject can be set to a string or array of strings to define one or more files to inject. Each path in inject should be relative to ${externalsOutputPath}/${to} or ${externalsOutputPath}/${from} depending on whether to was provided.

i18n-plugin

Rather than manually set locale data within an application's entry point, that data can instead be read from a config and injected in at build time.

The plugin accepts an options object with the following properties:

Property Type Optional Description
cldrPaths string[] Yes An array of paths to CLDR JSON modules that should be included in the build and registered with the i18n ecosystem. If a path begins with a ".", then it is treated as relative to the current working directory. Otherwise, it is treated as a valid mid.
defaultLocale string No The default locale.
supportedLocales string[] Yes An array of supported locales beyond the default.
target string Yes The entry point into which the i18n module should be injected. Defaults to src/main.ts.

A custom module is generated from the locale data, injected into the bundle, and then imported into the specified entry point. The user's locale is compared against the default locale and supported locales, and if it is supported is set as the root locale for the application. If the user's locale is not supported, then the default locale is used. For example, the user's locale will be used in the following scenarios:

The user's locale can be represented by the default locale:

  • Default Locale: 'en'
  • Supported Locales: none
  • User's locale: 'en-US'

The user's locale can be represented by one of the supported locales:

  • Default Locale: 'en'
  • Supported Locales: [ 'fr' ]
  • User's locale: 'fr-CA'

However, in the following scenario the default locale will be used, although it still will be possible to switch between any of the supported locales at run time, since their required data will also be included in the build:

  • Default Locale: 'en'
  • Supported Locales: [ 'de', 'ja', 'ar' ]
  • User's locale: 'cz'

service-worker-plugin

A custom webpack plugin that generates a service worker from configuration options, or simply ensures a custom service worker is copied to the output directory. Generated service workers support both precaching and runtime caching and allow you specify additional resources that should be loaded by the service worker.

The plugin accepts either a string path for an existing service worker to copy to the output directory, or an options object with the following properties:

Property Type Optional Description
bundles string[] Yes An array of bundles to include in the precache. Defaults to all bundles.
cachePrefix string Yes The prefix to use for the runtime precache cache.
clientsClaim boolean Yes Whether the service worker should start controlling clients on activation. Defaults to false.
excludeBundles string[] Yes An array of bundles to include in the precache. Defaults to [].
importScripts string[] Yes An array of script paths that should be loaded within the service worker
precache object Yes An object of precache configuration options (see below)
routes object[] Yes An array of runtime caching config objects (see below)
skipWaiting boolean Yes Whether the service worker should skip the waiting lifecycle

Precaching

The precache option can take the following options to control precaching behavior:

Property Type Optional Description
baseDir string Yes The base directory to match include against.
ignore string[] Yes An array of glob pattern string matching files that should be ignored when generating the precache. Defaults to [ 'node_modules/**/*' ].
include string or string[] Yes A glob pattern string or an array of glob pattern strings matching files that should be included in the precache. Defaults to all files in the build pipeline.
index string Yes The index filename that should be checked if a request fails for a URL ending in /. Defaults to 'index.html'.
maxCacheSize number Yes The maximum size in bytes a file must not exceed to be added to the precache. Defaults to 2097152 (2 MB).
strict boolean Yes If true, then the build will fail if an include pattern matches a non-existent directory. Defaults to true.
symlinks boolean Yes Whether to follow symlinks when generating the precache. Defaults to true.

Runtime Caching

In addition to precaching, strategies can be provided for specific routes to determine whether and how they can be cached. This routes option is an array of objects with the following properties:

Property Type Optional Description
urlPattern string No A pattern string (which will be converted a regular expression) that matches a specific route.
strategy string No The caching strategy (see below).
options object Yes An object of additional options, each detailed below.
cacheName string Yes The name of the cache to use for the route. Note that the cachePrefix is not prepended to the cache name. Defaults to the main runtime cache (${cachePrefix}-runtime-${domain}).
cacheableResponse object Yes Uses HTTP status codes and or headers to determine whether a response can be cached. This object has two optional properties: statuses and headers. statuses is an array of HTTP status codes that should be considered valid for the cache. headers is an object of HTTP header and value pairs; at least one header must match for the response to be considered valid. Defaults to { statuses: [ 200 ] } when the strategy is 'cacheFirst', and { statuses: [0, 200] } when the strategy is either networkFirst or staleWhileRevalidate.
expiration object Yes Controls how the cache is invalidated. This object has two optional properties. maxEntries is the number of responses that can be cached at any given time. Once this max is exceeded, the oldest entry is removed. maxAgeSeconds is the oldest a cached response can be in seconds before it gets removed.
networkTimeoutSeconds number Yes Used with the networkFirst strategy to specify how long in seconds to wait for a resource to load before falling back on the cache.

Strategies

Four routing strategies are currently supported:

  • networkFirst attempts to load a resource over the network, falling back on the cache if the request fails or times out. This is a useful strategy for assets that either change frequently or may change frequently (i.e., are not versioned).
  • cacheFirst loads a resource from the cache unless it does not exist, in which case it is fetched over the network. This is best for resources that change infrequently or can be cached for a long time (e.g., versioned assets).
  • networkOnly forces the resource to always be retrieved over the network, and is useful for requests that have no offline equivalent.
  • staleWhileRevalidate requests resources from both the cache and the network simulaneously. The cache is updated with each successful network response. This strategy is best for resources that do not need to be continuously up-to-date, like user avatars. However, when fetching third-party resources that do not send CORS headers, it is not possible to read the contents of the response or verify the status code. As such, it is possible that a bad response could be cached. In such cases, the networkFirst strategy may be a better fit.

Example

import ServiceWorkerPlugin from '@dojo/webpack-contrib/service-worker-plugin/ServiceWorkerPlugin';

new ServiceWorkerPlugin({
	cachePrefix: 'my-app',

	// exclude the "admin" bundle from caching
	excludeBundles: [ 'admin' ],

	routes: [
		// Use the cache-first strategy for loading images, adding them to the "my-app-images" cache.
		// Only the first ten images should be cached, and for one week.
		{
			urlPattern: '.*\\.(png|jpg|gif|svg)',
			strategy: 'cacheFirst',
			cacheName: 'my-app-images',
			expiration: { maxEntries: 10, maxAgeSeconds: 604800 }
		},

		// Use the cache-first strategy to cache up to 25 articles that expire after one day.
		{
			urlPattern: 'http://my-app-url.com/api/articles',
			strategy: 'cacheFirst',
			expiration: { maxEntries: 25, maxAgeSeconds: 86400 }
		}
	]
});

webpack-bundle-analyzer

A webpack plugin that provides a visualization of the size of webpack output with an interactive sunburst graphic. Functionally this is a copy of webpack-bundle-analyzer with a custom visualization.

The plugin accepts an options object with the following optional properties.

Property Type Description Default
analyzerMode 'server' or 'static' or 'disabled' Whether to serve bundle analysis in a live server, render the report in a static HTML file or files, or to disable visual report generation entirely. `'server'
analyzerPort number The port to use in server mode 8888
reportFilename string Path to the report bundle file. Multiple report will be created with -<bundleName> appended to this value if there is more than one output bundle. 'report.html'
openAnalyzer boolean Whether the report should be opened in a browser automatically true
generateStatsFile boolean Whether a JSON Webpack Stats file should be generated false
statsFilename string Name to use for the stats file if one is generated 'stats.json'
statsOptons any Options to pass to stats.toJson(). More documentation can be found here null
logLevel 'info' or 'warn' or 'error' or 'silent' The level of logs from this plugin 'info'

Example

import BundleAnalyzerPlugin from '@dojo/webpack-contrib/webpack-bundle-analyzer/BundleAnalyzerPlugin';

new BundleAnalyzerPlugin({
	analyzerMode: 'static',
	reportFilename: '../info/report.html',
	openAnalyzer: false
});

bootstrap-plugin

A custom webpack plugin that conditionally loads supported dojo shims based on usage and browser capabilities.

Supported Shims

  • @dojo/framework/shim/IntersectionObserver
  • @dojo/framework/shim/ResizeObserver
  • @dojo/framework/shim/WebAnimations

To use the plugin, use the provided bootstrap.js from @dojo/webpack-contrib/bootstrap-plugin as the application entry and add the plugin to the webpack configuration.

The BootstrapPlugin accepts requires the path to application entry point and an array of shimModules to process.

new BootstrapPlugin({
	entryPath: mainEntryPath,
	shimModules: [
		{
			module: '@dojo/framework/shim/IntersectionObserver',
			has: 'intersection-observer'
		},
		{
			module: '@dojo/framework/shim/ResizeObserver',
			has: 'resize-observer'
		},
		{
			module: '@dojo/framework/shim/WebAnimations',
			has: 'web-animations'
		}
	]
})

Transformers

registry-transformer

A custom TypeScript transformer that generates registry keys for widgets included in lazily-loaded bundles. This allows widget authors to use the same pattern for authoring widgets regardless of whether they are loaded from a registry or imported directly.

For example, if LazyWidget needs to be split into a separate bundle and this transformer is not applied, then LazyWidget would need to be added to the registry (registry.define('lazy-widget', LazyWidget)), and all calls to w(LazyWidget) would need to be updated to reference its registry key (w<LazyWidget>('lazy-widget')). By using this transformer, LazyWidget would instead be added to the registry at build time, allowing existing code to remain unchanged.

element-transformer

A custom TypeScript transformer that generates a custom element definition for specified widgets.

For example given a widget Hello,

import { v } from '@dojo/framework/widget-core/d';
import { WidgetBase } from '@dojo/framework/widget-core/WidgetBase';

interface HelloProperties {
	name: string;
	flag: boolean;
	onClick(): void;
	onChange(value: string): void;
}

export class Hello extends WidgetBase<HelloProperties> {
	protected render() {
		const { name } = this.properties;
		return v('h1', { }, [
			'Hello ${name}!'
		]);
	}
}

export default Hello;

The generated output would be ,

import { v } from '@dojo/framework/widget-core/d';
import { WidgetBase } from '@dojo/framework/widget-core/WidgetBase';

interface HelloProperties {
	name: string;
	flag: boolean;
	onClick(): void;
	onChange(value: string): void;
}

export class Hello extends WidgetBase<HelloProperties> {
	protected render() {
		const { name } = this.properties;
		return v('h1', { }, [
			'Hello ${name}!'
		]);
	}
}
Hello.__customElementDescriptor = { ...{ tagName: 'widget-hello', attributes: ['name'], properties: ['flag'], events: ['onClick', onChange'] }, ...Hello.prototype.__customElementDescriptor || {} };

export default Hello;

How do I contribute?

We appreciate your interest! Please see the Dojo 2 Meta Repository for the Contributing Guidelines.

Code Style

This repository uses prettier for code styling rules and formatting. A pre-commit hook is installed automatically and configured to run prettier against all staged files as per the configuration in the projects package.json.

An additional npm script to run prettier (with write set to true) against all src and test project files is available by running:

npm run prettier

Testing

To test this package, after ensuring all dependencies are installed (npm install), run the following command:

npm run test

Licensing information

ยฉ 2018 JS Foundation. New BSD license.

webpack-contrib's People

Contributors

agubler avatar bryanforbes avatar jameslmilner avatar kanefreeman avatar kitsonk avatar maier49 avatar matt-gadd avatar mwistrand avatar nicknisi avatar rorticus avatar tomdye avatar umaar avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

webpack-contrib's Issues

Block cache incorrectly included common chunk

Bug

When a common block is run for a specific chunk, if it is a common module then the cache result can end up being included into a common chunk such as main.js.

Expected

The output from a block should be inserted into the correct chunk.

Support adding to the pages to be visited in build time render during build time render

Enhancement
With blocks, it's now possible to discover content at build time rendering (ie reading a number of markdown files in a directory), this might mean that we want to then visit routes to get output from too. This is not currently possible due to the build time render paths being static in the dojorc. It'd be nice if it would be possible to dynamically add to those paths when running in build time rendering

Ensure dojo block cache data is only added to the correct bundle

Bug

At the moment the dojo block cache data will be added to every bundle that includes the block, even if the bundle wasn't executed or even loaded for the page.

We should only look in the executed bundles for each page to determine where to include the block cache.

JSX in Outlet render function error with Typescript `~2.9` and `^3.0`

Bug

JSX inside of an Outlet render function causes a compilation error with Typescript ~2.9 and ^3.0. Changing the JSX to the w() function fixes the issue.

Package Version:

Code

<Outlet key='home' id='home' renderer={() => <Home />} />

Expected behavior:

The build should compile without error.

Actual behavior:

The build fails with an error about attributes being undefined.

Stack Trace

c:\repo\node_modules\typescript\lib\typescript.js:62689
            var attrs = node.attributes.properties;
                                        ^
TypeError: Cannot read property 'properties' of undefined
    at visitJsxOpeningLikeElement (c:\repo\node_modules\typescript\lib\typescript.js:62689:41)
    at visitJsxSelfClosingElement (c:\repo\node_modules\typescript\lib\typescript.js:62681:20)
    at visitorWorker (c:\repo\node_modules\typescript\lib\typescript.js:62652:28)
    at visitor (c:\repo\node_modules\typescript\lib\typescript.js:62641:24)
    at visitNode (c:\repo\node_modules\typescript\lib\typescript.js:56836:23)
    at visitFunctionBody (c:\repo\node_modules\typescript\lib\typescript.js:56942:23)
    at Object.visitEachChild (c:\repo\node_modules\typescript\lib\typescript.js:57064:358)
    at visitorWorker (c:\repo\node_modules\typescript\lib\typescript.js:62658:31)
    at visitor (c:\repo\node_modules\typescript\lib\typescript.js:62641:24)
    at Object.visitNode (c:\repo\node_modules\typescript\lib\typescript.js:56836:23)
    at visitJsxExpression (c:\repo\node_modules\typescript\lib\typescript.js:62867:23)
    at transformJsxAttributeInitializer (c:\repo\node_modules\typescript\lib\typescript.js:62748:24)
    at transformJsxAttributeToObjectLiteralElement (c:\repo\node_modules\typescript\lib\typescript.js:62730:30)
    at Object.map (c:\repo\node_modules\typescript\lib\typescript.js:2025:29)
    at c:\repo\node_modules\typescript\lib\typescript.js:62699:49
    at Object.spanMap (c:\repo\node_modules\typescript\lib\typescript.js:2239:29)
    at visitJsxOpeningLikeElement (c:\repo\node_modules\typescript\lib\typescript.js:62697:46)
    at visitJsxSelfClosingElement (c:\repo\node_modules\typescript\lib\typescript.js:62681:20)
    at transformJsxChildToExpression (c:\repo\node_modules\typescript\lib\typescript.js:62670:28)
    at Object.mapDefined (c:\repo\node_modules\typescript\lib\typescript.js:2172:30)
    at visitJsxOpeningLikeElement (c:\repo\node_modules\typescript\lib\typescript.js:62712:188)
    at visitJsxElement (c:\repo\node_modules\typescript\lib\typescript.js:62678:20)
    at transformJsxChildToExpression (c:\repo\node_modules\typescript\lib\typescript.js:62668:28)
    at Object.mapDefined (c:\repo\node_modules\typescript\lib\typescript.js:2172:30)
    at visitJsxOpeningLikeElement (c:\repo\node_modules\typescript\lib\typescript.js:62712:188)
    at visitJsxElement (c:\repo\node_modules\typescript\lib\typescript.js:62678:20)
    at visitorWorker (c:\repo\node_modules\typescript\lib\typescript.js:62650:28)
    at visitor (c:\repo\node_modules\typescript\lib\typescript.js:62641:24)
    at visitNode (c:\repo\node_modules\typescript\lib\typescript.js:56836:23)
    at Object.visitEachChild (c:\repo\node_modules\typescript\lib\typescript.js:57060:45)
    at visitorWorker (c:\repo\node_modules\typescript\lib\typescript.js:62658:31)
    at visitor (c:\repo\node_modules\typescript\lib\typescript.js:62641:24)
    at visitNode (c:\repo\node_modules\typescript\lib\typescript.js:56836:23)
    at Object.visitEachChild (c:\repo\node_modules\typescript\lib\typescript.js:57124:46)

When using a feature set resolve `has.add` statically

Enhancement

Currently the static-build-loader statically resolves usages of has and exists for a given set of flags, these flags are either provided via the users .dojorc or to a static set for evergreen browsers.

We should also consider resolving calls to add to the defined flag value for consistency, also this will reduce the overall size of the has module for evergreen builds.

// @dojo/framework/core/has.ts

add('my-test', () => {
// lots of code to determine the `has` value
});

would become, assuming the static flag for my-test is set to true:

// @dojo/framework/core/has.ts

add('my-test', true);

Use Puppeteer for Build Time Rendering

Enhancement

Build Time Rendering uses JSDOM currently to generate the build time rendering, JSDOM can be quite limiting for any applications that load or use any DOM specific APIs such MutationObservers or IntersectionObservers. Switching to puppeteer will provide more support for DOM APIs and more flexibility for future enhancements such as fully supporting using the history API for routing.

No emit or blocks only mode for build time rendering

Enhancement

It can be desirable to use build time rendering to execute blocks during build time but not require the css and html to be inlined. We should consider adding a blocksOnly or noEmit that only runs the blocks part of BTR without inlining the html/css.

Support jsdom as a Build Time Renderer

Enhancement
Rendering many pages using Puppeteer can be very slow. We should consider trying jsdom to speed this up and offering it as an alternative or default renderer. To implement we might want to write a small api wrapper around jsdom that matches puppeteer to make the implementation tidier.

Support static has pragma

Enhancement

We should support static has pragma, where if the condition is true at build time, the next import statement is elided.

For example if a module contained:

'has("foo")';
import 'foo';

And the foo feature was true at build time, import 'foo'; would be removed from the module definition. If foo was false at build time, the module would remain the same.

This will allow us to elide polyfills and other imports in builds, to make builds more efficient.

@matt-gadd and I discussed and it is near impossible to build a webpack plugin that can properly elide a dependency after the dependency graph has been build, and that a loader will be needed to rewrite the module before being analysed by webpack for dependencies.

Additional static option "heuristic" for Build Time Render

Enhancement
Add another option to the static flag named heuristic or auto, which will auto-detect per path whether a page can be static. The heuristic to determine whether a page could be static would be us tracking any of these calls:

  • fetch/xhr
  • any events (other than an anchor which has a valid href)
  • setInterval/setTimeout
  • any dom mutations?

Build Time Render should take into account build hash changes

Bug

Code
As part of the new node execution support in build time render, we now cache those results and inject them into the appropriate webpack bundles. This means that the bundle has changed content wise, but we do not update the hash of these bundles to reflect it. This effectively means a user could ship 2 bundles with identical hashes that actually have differing content which is bad.

To fix, if we do modify bundles we should calculate new hashes for any of the affected.

CSS chuck load error when folder name matches route name

Bug

Code

https://github.com/dojo/site

Expected behavior:

The build should succeed no matter what folder the split code is in.

Actual behavior:

If the folder has the route name in it, it produces the following error.

BTR runtime Error: Loading CSS chunk src/tutorials/Tutorial failed.
(http://localhost:60177/src/tutorials/Tutorial.css)
    at HTMLLinkElement.linkTag.onerror (http://localhost:60177/tutorials/bootstrap.js:125:25)

Defined Routes:

"tutorials/sample-tutorial",
"tutorials/local-installation",
"tutorials/another-tutorial",
"tutorials",

Folder Names Tried:

  1. src/pages/tutorials/Tutorials.tsx -- ERROR
  2. src/tutorials/Tutorials.tsx -- ERROR
  3. src/pages/Tutorials.tsx -- Builds successfully

Statically generate index.html files for each route when using history API

Enhancement

Currently build time rendering supports applications that use hash routing. This runs the application bundles and writes the html and critical css for each route to the applications index.html. For hash routing we support multiple routes by injecting a small script into the index.html that appends the correct html segment depending on the route.

However build time rendering does not currently support applications using the history API for routing (StateHistory provided by @dojo/framework/routing). Build time rendering needs to be enhanced such that we generate a separate index.html in the directory that reflects the route in the build output:

  1. The static HTML for the route is written to the index.html.
  2. Critical CSS required to load the route is added to a style in the index.html.
  3. Relative src references in the HTML are amended to ensure that resources are loaded correctly.
  4. Relative url references in the CSS are amended to ensure that resources are loaded correctly.
  5. The src of the scripts/css are amended with a relative prefix so they can be loaded.

static-build-loader should transform `exists`

Currently the static-build-loader will transform certain has() calls to literal values at build time. For example if const hasFoo = has('foo') is set to true, we will transform it to const hasFoo = true. Because then there is no interaction with has itself, using exists('foo') would still result in false as it is never registered.

The easiest fix for this would be to also statically resolve exists() where possible to a boolean value too.

Refs dojo/framework#100

README review

Enhancement

  • We should give a light introduction to what this repo is useful for and where it is used.
  • There are no overviews of the css-* loaders/plugins
  • No overview of the promise-loader
  • There is stock writing in the licensing section

Make Build Time Render render pages in parallel

Enhancement
Rendering many pages in Build Time Render can take a lot of time due to them running sequentially. In theory we should be able to run them in parallel as they are self contained.

Create a typescript custom transformer for auto registry items

Enhancement
One of the most useful parts of registries in widget-core, is the ability to lazy load widgets. At the moment to do this, you need to change the form of the w call from:

w(MyWidgetClass, {});

to

w<MyWidgetClass>('my-widget-class', {});

This will allow TypeScript to elide the import as it's only used as a type. The user will also then have to register my-widget-class to the registry somewhere.

Although this works, it would be nicer if the user didn't have to change their code at all. We instead take the configuration we already have from the .dojorc for bundles, and do the above work for them.

This would mean modifying the source code from something like this:

import WidgetBase from '@dojo/widget-core/WidgetBase';
import { w } from '@dojo/widget-core/d';
import Foo from './widget/Foo';
import Bar from './widget/Bar';

export default class Baz extends WidgetBase {
	render() {
		return v('div', [
			w(Foo, {}),
			w(Bar, {})
		]);
	}
}

to:

import WidgetBase from '@dojo/widget-core/WidgetBase';
import { w } from '@dojo/widget-core/d';
import { registry } from '@dojo/widget-core/decorators/registry';

const __registryItems__ = {
	'unique_identifier_a': () => import('./widget/Foo'),
	'unique_identifier_b': () => import('./widget/Bar')
};

@registry(__registryItems__)
export default class Baz extends WidgetBase {
	render() {
		return v('div', [
			w('unique_identifier_a', {}),
			w('unique_identifier_b', {})
		]);
	}
}

Create a TypeScript transformer to autogenerate properties and attributes for Custom Elements

Enhancement
The current custom element target requires a decorator explicitly stating the properties and attributes for the element like so:

@customElement({
	tag: 'dojo-input',
	properties: [ 'theme', 'aria' ],
	attributes: [ 'placeholder' ],
	events: [ 'onBlur' ]
})

this can be quite laborious especially given users will have already stated the public api via a properties interface.

the proposal would be to use the typescript checker to infer the properties and attributes from the widgets property interface and write the according decorator detail for them, for a user authoring the decorator they would only need to provide a tag name like so:

@customElement({ tag: 'dojo-input' })

we'd also gain the other advantage that we'd be able to actually know the types of the properties so we can perform transformations for attribute types other than a string

Has flags not statically resolved for Promise shim used by bootstrap plugin

Bug

The bootstrap plugin is explicitly excluded from the static-build-loader meaning that has flags are not resolved at build time. Currently the async.js module uses the Promise shim to ensure support for IE11 but doesn't not resolve the has flag based on the builds features flags. This needs to be removed from the async.js bootstrap module and added to the builds entry to ensure Promise is available for IE11 and the has flags are correctly resolved at build time.

BTR incorrectly filtering out CSS

Bug

BTR is incorrectly filtering out CSS with nested selectors.

.my-class > a {
    background: red;
}

Should consider stripping anything after the > character when matching against the class list for filtering.

Bug: CSS modules in entry break watch

Bug

When running webpack in watch mode with the css-module-dts-loader, changes to an entry point trigger a rebuild, but the entry itself is not recompiled if it imports a CSS module. If I change import * as css from './main.css' to const css = require('./main.css'), or if I comment out the usage of css-module-dts-loader for TypeScript files in the webpack config, then changes are correctly reflected in the output.

Code

// src/main.ts
import * as css from './main.css';

console.log(css);

Run a build in watch mode (e.g., with cli-build-app: dojo build -w -m dev), and then make a change to the file. For example, change to:

// src/main.ts
import * as css from './main.css';

console.log('Update!', css);

Expected behavior:

The change should be reflected in the output build (or in the browser if using the webpack-dev-server).

Actual behavior:

The rebuild is logged to the console, but no change is reflected in the output.

Migrate from Plugin to Loader

@kitsonk commented on Fri Sep 29 2017

Enhancement

The architecture of static-optimize-plugin is limited in being a plugin. In particular webpack does not provide easy ways for plugins to elide dependencies and update a dependency graph. The only effective way to remove dependencies is remove them from the source as webpack loads the module. This means that the features really should be a loader instead of a plugin.

@matt-gadd's umd-compat-loader provides an example of loading a module, doing an AST transform on the module which also updates the source map, as a webpack loader. We should follow a similar architecture for the features in this package.

The connection to http://localhost:9999/__webpack_hmr was interrupted while the page was loading.

Bug Getting the following error using Firefox 69.0.2 (64-bit). The error specifically is:
The connection to http://localhost:9999/__webpack_hmr was interrupted while the page was loading. I am not getting this error when I load the app on chrome.

Package Version: 6.0.0

Code

I paired down the code to a very simple app and this error still occured:
app.tsx

import icache from '@dojo/framework/core/middleware/icache';
import store from './middleware/store';
import { ensure } from './middleware/ensure';

const factory = create({ store, icache, ensure });

export default factory(function App({ middleware: { store: { get, path }, icache, ensure }}) {
        return (
            <div> Sample </div>
        )
});

main.tsx

import renderer, { tsx } from '@dojo/framework/core/vdom';
import App from './app';

const r = renderer(() => <App />);
r.mount();

Static has loader treats default import as namespace

Bug

The static build loader attempts to handle pulling in has using either require or import, but it treats import has from '/has' the same as var has = require('/has'), and ignores import * as has from '/has'.

The result is that in the following code the two statements are treated as identical:

import has from '@dojo/framework/core/has';

has('foo');
has.default('foo');

And in the following code the statically defined features would be ignored and the code would remain unchanged:

import * as has from '@dojo/framework/core/has';

has.default('foo');

Better support for code splitting block results

Enhancement
At the moment we put block results in to bundle they were used in. Although this works, if you're using a single block multiple times in the same place, all the results will go into the same bundle making the bundle potentially very large. It would be ideal if each block result got it's own bundle and was lazily loaded.

Allow relative CLDR paths in the dojorc

Bug

The I18nPlugin currently does not allow any relative value in the cldrPaths rc option. This is probably fine in many applications since CLDR data will likely be supplied by a third-party module. However, in the event that CLDR data is provided by the application itself, it would be best if relative paths were assumed to be relative to process.cwd().

Code

// .dojorc
{
    "build-app": {
        "cldrPaths": [ "./src/nls/cldr-data.json" ]
    }
}

Expected behavior:

The JSON file ${process.cwd()}/src/nls/cldr-data.json is bundled with the application.

Actual behavior:

The build throws the error, Cannot find module './src/nls/cldr-data-.json'

Use `@types/webpack`

Enhancement

Since dojo/cli-build-app uses @types/webpack, and there are conflicts between that and webpack-contrib's webpack.d.ts, @types/webpack should be added to this project to ensure that it can be used by cli-build-app.

Catch errors from build-time-rendering and fail gracefully

Enhancement

Currently with build-time-rendering if the application fails on load then it throws and error and causes the build to fail, we should consider adding a way to catch the errors and fail gracefully.

Ideally reporting back to the user that the build time rendering has failed.

Move dependencies from `devDependencies` to `dependencies`

Bug

There are a number of dependencies that are incorrectly listed as development dependencies in package.json. For the most part, this has gone unnoticed as there is some overlap between cli-build-{app|webpack} and webpack-contrib. However, ExternalLoaderPlugin cannot be used without first installing missing dependencies, and the dts loader only works because typed-css-modules and ts-loader are included as dependencies of cli-build-app and cli-build-webpack (although at this point I don't think cli-build-app needs typed-css-modules).

Add promise loader implementation that deals with absolute paths

Enhancement

The current promise-loader implementation that cli-build-app requires, does not remove absolute paths for source map content which means that builds are not consistent across environments and also means the information is accessible.

The dependency hasn't been updated in 2 years and there are outstanding pull requests that have not been reviewed so it is unlikely that any change will be accepted and released (in a reasonable time)

The proposal is to take the implementation and enhance it to remove absolute path references using loader-utils#stringifyRequest and convert to typescript.

Support build time rendering in single bundle mode

Enhancement

Add support for running build time rendering in single bundle mode. This means there will not be a runtime/blocks.js bundle that is currently used to load the blocks lazily. Implementing a sync mode that inlines all the blocks and loading mechanism into the main.js bundle should enable build time rendering in single-bundle mode.

`I18nPlugin` not compatible with `NamedModulesPlugin`

Bug

When used in conjunction with the NamedModulesPlugin, the I18nPlugin does not correctly set the mid for the setLocaleData template. That is, there is no type check on the module ID, so it is blindly output as if it were a number: __webpack_require__(/path/to/module).

Expected behavior:

It should output the mid as a string wrapped in quotes.

Actual behavior:

It outputs the mid without any quotes, and as such results in an error.

Public path, Origin and App Base are not scoped per project

Bug
We currently use a number of global vars to set some internal properties such as public path in the bootstrap plugin. Because these are not scoped per project it's impossible to set them individually. We should consider wherever we need to leverage global variables, that we put them on at least a scope of the project. We can leverage libraryName for this like we have elsewhere

Trouble with sourcemaps from istanbul-loader

Bug

Using istanbul-loader fixes the coverage report from istanbul in @dojo/widgets, but it would appear that something it's doing is messing up the source maps. @matt-gadd confirmed that the source maps are being generated, but if you open the file in Sources tab in Chrome, you'll find that the source map is empty.

** Steps to reproduce**

  • From @dojo/widgets, dojo build widget -m unit
  • Then dojo test -c local
  • The dojo test command will print out a URL when it's finished, copy this URL!
  • Start a web server from the root widgets directory
  • Navigate to the URL in your clipboard
  • Open up the developer tools, Sources, load a widget!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.