Add --target=lib flag - #42
Conversation
agubler
left a comment
There was a problem hiding this comment.
A few observations. Also how does this behave with being ejected?
| postcssCustomProperties({ preserve: 'computed' }) | ||
| ]); | ||
|
|
||
| return globby(cssFiles).then(files => { |
There was a problem hiding this comment.
no await? Might be a bit clearer.
There was a problem hiding this comment.
That would improve readability here.
| } | ||
|
|
||
| const spinner = ora().start('Cleaning previous output'); | ||
| return createTask((callback: any) => rimraf(outDir, callback)) |
There was a problem hiding this comment.
All these could be awaited also perhaps?
There was a problem hiding this comment.
I'll see what I can come up with, but personally I prefer the then series to a try/catch block in this particularly case.
| postcssImport(), | ||
| postcssPresetEnv(postcssPresetConfig), | ||
| postcssModules({ | ||
| generateScopedName: mode === 'dist' ? '[hash:base64:8]' : '[name]__[local]__[hash:base64:5]', |
There was a problem hiding this comment.
We have standardised on just '[name]__[local]__[hash:base64:5]' now for all modes for the other build commands
There was a problem hiding this comment.
Thanks, I should have looked at what we were already using in the repo instead of what @dojo/widgets was using from @dojo/grunt-dojo2.
| const outDir = `output/${mode}`; | ||
| const cssFiles = path.join(basePath, outDir, '**/*.css'); | ||
| const packageJsonPath = path.join(basePath, 'package.json'); | ||
| const packageJson = fs.existsSync(packageJsonPath) ? require(packageJsonPath) : {}; |
There was a problem hiding this comment.
Should we support building the a widget lib without a package json?
|
Also does this mean that building to the library target doesn't support watching (and maybe other options)? How is this going to work with |
|
@agubler I will look into adding a watch option as well as other flags available for the webpack build. As mentioned in the description, this does not yet work with eject, as it is independent of the webpack build, so we'd need provide a separate way to build libraries once ejected. I have this on my to-do list, but wanted to get this up for at least an initial review. |
|
@mwistrand I should have read the description re |
|
There are still a couple minor annoyances I need to resolve around logging (as well as an additional fix), but this is now compatible with |
|
@mwistrand coming together 👍 Would be great if the output was formatted like the output from the custom element target... Would that be possible? |
b35abc9 to
fec5b12
Compare
|
@agubler After playing around with this a bit, I came up with what you see below. Note that we still need to determine how to best use this with |
12c3ed7 to
c12c2d6
Compare
Codecov Report
@@ Coverage Diff @@
## master #42 +/- ##
=========================================
- Coverage 70.67% 64.5% -6.18%
=========================================
Files 8 8
Lines 358 400 +42
Branches 59 83 +24
=========================================
+ Hits 253 258 +5
- Misses 105 142 +37
Continue to review full report at Codecov.
|
|
@mwistrand At the moment we can only build either |
|
Also I think we'll want to minify the output css (we use cssnano currently in cli-build-app and dojo/widgets) |
|
Could we configure the |
c12c2d6 to
cb1683a
Compare
|
@agubler I've pushed changes that will 1) generate an entry point from the |
cb1683a to
224864a
Compare
|
Thanks @mwistrand! Let me know when it's ready for review again :) |
|
One thing can we rename the |
f112f91 to
936b7f4
Compare
| const basePath = process.cwd(); | ||
| libEntryPath = path.join(basePath, 'src', 'main.ts'); | ||
| if (!fs.existsSync(libEntryPath)) { | ||
| const tmpDir = fs.mkdtempSync(os.tmpdir()); |
There was a problem hiding this comment.
Thanks for your work on this. It's going to be very useful!
Whilst trying to get this setup I've found this line doesn't work on linux as os.tmpdir does not include the path separator. This means the build tries to make a folder in the root of the filesystem.
util.ts in this repo already works around it with:
fs.mkdtempSync(`${os.tmpdir()}${path.sep}`)
I'm curious about on which OS this works for you?
There was a problem hiding this comment.
Thanks for catching this. It's been working for me on macOS and Windows 10/11, but I'll update this as well.
|
Are there any patches required in a In my dev build EDIT: I'm sorry if that's really unclear I'm a pretty lost in all this complex build stuff.
|
agubler
left a comment
There was a problem hiding this comment.
@mwistrand This is super close, just a few suggestions and a heads up that we are probably going to land #56 (changes the CE builds to using a single configuration rather than a configuration for each element), which will mean this will need a bit of a rebase (shouldn't be too much!).
Also have been thinking about the issue with creating a legacy and modern build together, for now to reduce the complexity I am going to reverse a bit and say that we should only support producing one at a time. It will be down to the consumer to run the command twice and package (copy, move etc) the output as needed for their final release bundled.
| return entry; | ||
| }, {}), | ||
| mode: args.target === 'lib' ? 'none' : 'development', | ||
| entry: |
There was a problem hiding this comment.
Instead of creating a temporary file or using an existing main.ts as the entry point, we could iterate through the widgets and use the widget.path as the entry point?
For filtering the webpack assets we could prefix the js and css output name with something like throw-away, which would leave all the other assets.
There was a problem hiding this comment.
Something like
elements.reduce((entry: any, element: any) => {
entry[element.name] = [element.path];
return entry;
}, {});| const outputPath = output!.path as string; | ||
| const modeOutputPath = path.join(outputPath, location); | ||
|
|
||
| if (module && args.target === 'lib') { |
There was a problem hiding this comment.
Can we do this in the base and change based on mode and target? It would save having to have a duplication to edit the ts-loader configuration.
In another change @rorticus is turning transpileOnly off for building custom elements, so that wouldn't need to be updated for the lib target.
| config.plugins = removeEmpty([ | ||
| ...plugins!, | ||
| (args.target !== 'lib' || args.clean) && new CleanWebpackPlugin([location], { root: outputPath, verbose: false }), | ||
| args.target === 'lib' && |
There was a problem hiding this comment.
Can we include the EmitAllPlugin in the base config as well and set inlineSourceMaps to false for both dev and dist outputs.
| return child.assets.map((asset: any) => { | ||
| const size = (asset.size / 1000).toFixed(2); | ||
| return `${entry}/${asset.name} ${chalk.yellow(`(${size}kb)`)}`; | ||
| const assetName = isLibrary ? asset.name.replace(/^\//, '') : `${entry}/${asset.name}`; |
There was a problem hiding this comment.
How come we need to strip the leading / here?
|
|
||
| const basePath = process.cwd(); | ||
| libEntryPath = path.join(basePath, 'src', 'main.ts'); | ||
| if (!fs.existsSync(libEntryPath)) { |
There was a problem hiding this comment.
As per the comment above, if we use the element.path as the module to load we shouldn't need to create any temporary files.
|
@mwistrand |
|
@willstott101 If you're CSS is getting mangled then something is wrong; you shouldn't have to change anything to your app to use a library built with |
|
@willstott101 I haven't yet explored much more deeply than this, but the correct CSS classes are preserved when building The Since |
|
Thankyou very much for looking into this. It's quite possible I've got something wrong. I can't get at my work repos till Monday, but I can say that I'm trying to theme the imported widgets. I'll try and use the widgets from my lib without themeing and see if the class names still change. Should the names stay the same even if they're being themed by another ( |
62c7a87 to
2f04e46
Compare
| "chalk": "2.4.1", | ||
| "clean-webpack-plugin": "1.0.0", | ||
| "cli-columns": "3.1.2", | ||
| "copy-webpack-plugin": "^4.6.0", |
| getLocalIdent | ||
| modules: true, | ||
| sourceMap: true, | ||
| url: args.target !== 'lib' |
There was a problem hiding this comment.
It prevents css-loader from replacing url() values for library builds.
There was a problem hiding this comment.
don't we want to resolve url's for library builds? why should the css be different in this mode?
There was a problem hiding this comment.
Since I imagine we want libraries to be able to use the url(~from/node_modules) syntax, then we do need to resolve the URLs. At the time, I experienced some difficulty getting a clean match between the replaced url() path and the actual location of the asset. For example, if the CSS file is theme/icon.m.css, then the font it imports would be output to output/{mode}/name.woff, but output/{mode}/theme/icon.m.css would import it as url(name.woff) instead of ../name.woff. Fortunately, a newer version of MiniCssExtractPlugin allows the publicPath option to be a function, which effectively mitigates this issue (so the font would be imported as url(../name.woff)).
3f0cae7 to
ed0626a
Compare
| describe: 'the type of project', | ||
| alias: 't', | ||
| default: 'widget', | ||
| choices: ['widget', 'lib'] |
There was a problem hiding this comment.
should we perhaps change widget to custom element now?
| config.plugins = [...plugins!, new CleanWebpackPlugin([location], { root: outputPath, verbose: false })]; | ||
| config.plugins = removeEmpty([ | ||
| ...plugins!, | ||
| (args.target !== 'lib' || args.clean) && new CleanWebpackPlugin([location], { root: outputPath, verbose: false }) |
There was a problem hiding this comment.
I can't see where this args.clean is set, now that we are building the dev and dist separately do we actually need this different behaviour anymore?
There was a problem hiding this comment.
I believe it can be removed (from both here and the dist config), but I'll double-check before blindly doing so.
| ]); | ||
|
|
||
| if (args.target === 'lib') { | ||
| config.devtool = 'source-map'; |
There was a problem hiding this comment.
isn't this the default for production anyway? if so we can just set it for both and drop the if as it won't harm.
| } as any), | ||
| args.target === 'lib' && | ||
| new CopyWebpackPlugin( | ||
| ['src/**/*.css.d.ts', assetGlob].map(from => ({ |
There was a problem hiding this comment.
won't any assets in the webpack build get copied over anyway?
09a9603 to
548c956
Compare
|
@mwistrand Re the hashed assets like fonts, doesn’t everything reference the assets non hashed and by the original paths? How do the hashed asset names work? |
|
@mwistrand Sorry I've given you conflicts again :( I took the |
|
@agubler The |
Add a `--target=lib` flag that, when set, compiles and emits the specified widgets as individual files within a library. Add Cypress tests Tests behave as follows: - Build the lib - Copy the output back into the test-app - Build the test app - Execute tests against that application
- Fix: the custom-element.js template should be copied to `config/build-widget` on eject. - Add `--env.target` to the ejected config - Generate single configs for all elements instead of mapping each element to a separate webpack config
a7a8260 to
206ffd2
Compare
|
@mwistrand Okay, I think that's okay - I don't think I have any comments left! Have we tried it with |
|
@agubler I’ve been using |
|
@mwistrand perfect!! |
|
@mwistrand Let me know when you've done the sanity check 😄 |
As managing d.ts files for library builds will be handled by the EmitAllPlugin, the copy-webpack-plugin and corresponding types packages are no longer necessary as dependencies.

Resolves #35
Allow widget libraries to be built with
--target=lib(or-t libfor short). With this flag, all TypeScript and CSS files beneathsrc/are processed and output tooutput/{mode}, along with any font and/or image assets. Although this does not utilize webpack, the--legacyflag is still used to determine the CSS and TypeScript targets.At the moment this is excluded from
ejectsince it is entirely independent of the webpack build used to bundle custom elements, although at some point it would be worthwhile to determine the best way to handle such cases.