How to avoid having injected CSS styles discarded by tree shaking? - javascript

I am using Rollup to bundle a React component. This component is later used in a React application bundled with Webpack. The component uses styles defined in two CSS modules -- one containing global styles and one with specific styles for the component itself.
To bundle the CSS modules I use rollup-plugin-postcss with default options. This means the CSS modules are converted into JS files, which are later injected into the document using style-inject.
To provide a more clear picture this is how my project looks like:
|- examples/example // this is the example App
|- packages/test-css // this is the component
| |- src
| | |- index.module.css // global styles
| | |- index.ts // this file imports index.module.css
| | |- myComponent.module.css // component-related styles
| | |- myComponent.tsx // this file imports myComponent.module.css
The Rollup bundle does contain the code related to style injection both for the global and component-related styles. But,the Webpack bundle for the application only contains the component-related styles (the contents of myComponent.module.css are present, but not the ones from index.module.css).
I have tried excluding *.css.mjs files (the ones generated by Rollup) from being tree-shaken, but I still have the same issue. Since the React application directly imports MyComponent it seems Webpack bypasses index.ts (where the global styles are imported) altogether.
Is there a way to instruct Webpack not to discard the CSS modules imported in index.ts without completely disabling tree shaking? (if tree shaking is disabled all the styles are loaded).
I am including a repository to reproduce the example: https://github.com/xorxsan/test-css.

Related

ServiceWorker in React and Typescript

When creating javascript-based React projects, installing a Service Worker is just a matter of changing serviceWorker.unregister() to serviceWorker.register() on index.jsx.
With such project structure:
- src
|- index.jsx
|- serviceWorker.js
When this code gets built, the /build folder will look as followed:
- build
|- static
| |- css
| |- js
| |- media
|
|- index.html
|- service-worker.js
This will work fine and will result in the Service Worker being registered properly.
On the other hand, when setting up a project in Typescript, given the same project structure (where the js / jsx files are ts / tsx instead), the /build will look something similar:
- build
|- static
| |- js
| |- bundled-js.chunk.js
| |- bundled-js2.chunk.js
| |- ..
|
|- index.html
So it seems Typescript builds the serviceWorker bundling it with all the other js files.
This will then result in the Service Worker not being registered, with the following error in console:
Error during service worker registration: DOMException: Failed to register a ServiceWorker for scope ('https://example.com/') with script ('https://example.com/service-worker.js'): The script has an unsupported MIME type ('text/html').
The live site with the error can be seen here; The open source code can be found here.
Any idea of what I may be doing wrong?
Any tip is greatly appreciated, thank you in advance!
Ok, looking at your code your using Create React App,. This uses webpack, and it's that what is bundling, it's nothing to do with Typescript, the same would happen for Javascript when it gets bundled.
The problem is overriding the webpack config's is not easy, there is CRACO -> https://github.com/gsoft-inc/craco/blob/master/packages/craco/README.md#webpack-api
But even then it's a little tricky!! You need to tell webpack not to bundle service-worker.js, Also I would take the register bit out of the server-worker, as that can be bundled and wants to be included in your bundle.
The simplest option might be just to compile the webworker separately with Typescript directly. And again, keep the register bit separate inside your normal build.

TypeScript: Can I import a folder without having to write an index.ts file?

If most directories of a project contain no more than 2-3 TypeScript files and all of their exports should be accessible when importing the containing directory somewhere else, this results in a lot of index.ts files with predictable content.
Example
Directory: my-component
my-component-config.ts
my-component.ts
index.ts
What does index.ts contain? Of course, it contains
export * from "./my-component-config"
export * from "./my-component"
Which is obvious.
For 10 component directories, that means: 10 index.ts files containing 100% redundant information.
How can I make TypeScript(/Node) implicitly create index.ts files on the fly, that need not be stored on the hard disk?
Component isn't a well defined concept in TypeScript & node.js, but module and package are.
In general, module is a source file, let's ignore the exceptions. So by creating index.ts files per directory, you are generating façade modules aggregating only a few file/modules each. If all you are looking to do is organize your source files into logical components, you don't need the per-directory façade, you can simply import each file individually rather than a directory at a time.
At a higher level, if you have a package that consists of a number of different directories, it can have a single index.ts façade at package-level. That file would exported each file/module just once, no need for index.ts per directory. So this might look like (assuming each is a .ts file):
export * from './IntStream';
export * from './misc/Interval';
export * from './misc/IntervalSet';
export * from './Lexer';
...
I don't think there's a way to import a directory in TS without and index file
check these questions if you haven't
How to import all modules from a directory in TypeScript?
Typescript 1.8 modules: import all files from folder
I think the best approach is to write a script to generate index.ts that imports all files in the directory, and run that script every time you add/remove a file.

Webpack - How to alias dynamically according to file location?

I have a project tree where many subparts that are similar. It looks like this:
root
|-- editor
|- components
|- services
|- utils
|-- dashboard
|- components
|- services
|- utils
I want to alias 'components' (for example), to match editor/components when the requiring file is inside editor, and dashboard/components when the requiring file is inside dashboard.
Is there a way to do it (resolve dynamically)? Or do I have to assign separate aliases?

avoiding deep nested module imports in an ES6 JavaScript web project

I am trying to tackle that I am currently have with my JavaScript project structure. I am writing ES6 syntax with webpack. My current directory structure looks like this
project-dir
|_ packages.json
|_ webpack.config.js
|_ html
| |_ ***
| |_ ***
|_ js
|_ app.js
|_ routes
|_ abc-component
| |_ components
| | |_ abc1.js
| | |_ abc2.js
| |_ index.js
|
|_ xyz-component
| |_ components
| | |_ xyz1.js
| | |_ xyz2.js
| |_ index.js
|_ reducers.js
This is simpler structure. But the problem is that the "reducers.js" and the "abc1.js/xyz1.js" need access to the same functionality. So, if that functionality lives in "reducers.js" then the "abc1.js/xyz1.js" would have to import it as "../../reducers.js". If that functionality lives split up in "abc1.js" and "xyz1.js" etc, then "reducers.js" will have to import each one of them as ".\abc-component\components\abc1.js" and ".\xyz-component\component\xyz1.js". The first way, it feels like i am reaching way up and the second way, it feels like i am reaching way up. The code is also constrained to the structure of the file layout. This directory structure could go deeper down and we will end up with very ugly imports.
So, it makes sense for me to pull out this functionality that is shared by reducers.js and "abc1/js/xyz1.js" into a different module. In doing that i thought about putting that in a another git repo, but this functionality is very project specific and didn't want the hassle of putting it in another repo.
I tried creating a "lib" folder under the "js" folder and put the common functionality it in that folder along with a packages.json file. That way, i thought i would just add it as a "dependencies" in the project's package.json file with the "file:\lib\common" value. This way, it will reference that module locally. But this landed me in a load of trouble. The problems i am encountering are 1) I couldn't write ES6 in this common module since webpack is not processing it 2) Everytime I change something in the common.js, i have to run "npm install" 3) npm seems to be caching an old version of the common module ever after i change it and run "npm install". Not sure where it comes from, i did clean the npm cache by running "npm cache clean" 4) Even when i just write old javascript in this common module and not ES6, webpack watch does not pick up on changes to this file and re-render the app.
I was wondering if i could get some thoughts on how best to go about solving this problem. Also, thoughts about how i tried and exceptionally failed in solving the problem with a local module would be very helpful.
One way to deal with this is to put your common code into a node module and just import it like you would any other library.
This would work similarly to the "lib" idea you suggested but without needing relative imports.

Excluding references from Typescript compilation?

I have a bit of a strange (but in my view sensible) scenario.
I have a web site, mobile application and maybe going forward a web server all written in Javascript. I have a huge chunk of functionality which is shared between all these systems. This shared stuff would be models, repositories, business logic etc etc.
If we exclude the web server bit as that is a future idea, the web application has a directory structure like this:
app
|- assets
|- models
|- services
|- migrations
|- view-models
|- views
|- descriptors
Now each of these applications is broken down into 2 areas, the core and ui sections, the core is the re-usable stuff such as models, services, migrations and the ui stuff is ONLY used for that application which would comprise of view-models, descriptors (Incase you are wondering views are all html and assets are css, images etc).
Before I was adopting typescript I had a build script which would basically combine all the core files together and minify them. Then it would combine all the UI ones together and minify them. That way in the mobile application I can then just use the my-app.core.min.js and everyone is happy, I am reusing all the re-usable components from the main web application. However I do not need the ui stuff as the mobile UI is completely different to the main web ui, and the web service would not even have a UI going forward.
SO!
With that context explained lets jump back to the Typescript problem at hand. Currently the typescript files are compiled by tsc.exe (version 0.83) via a build script, which just wraps the interaction.
So in the new Typescript world the structure now has a references folder like so:
app
|- assets
|- models
|- services
|- migrations
|- view-models
|- views
|- descriptors
|- references <- NEW ONE!
This references folder is automatically populated by the build script with all local references, so it will trawl through the whole directory tree find all typescript files and build a huge reference file, which is a file full of the reference declarations for local typescript file, to find out more about what im on about look over this question:
Can you create Typescript packages? like c# dlls
So now when I run the build script the following steps happen:
Compiling for Core
Find all *.ts files within the models, services, migrations folders and subfolders
Add all the previous files into an array and also add in the reference files
run tsc.exe with a command like so tsc.exe --out <something>.core.js <previous_file_list>
Compiling for UI
Find all *.ts files within the view-models, descriptors folders and subfolders
Add all the previous files into an array and also add in the reference files
run tsc.exe with a command like so tsc.exe --out <something>.ui.js <previous_file_list>
Now I was expecting this to output 2 files my-app.core.js which ONLY contained the core files, and a my-app.ui.js which ONLY contained the ui files. However they both include everything...
Now after thinking about this, it must be due to the references, as they are both referencing all files, however thats just a compilation dependency in my eyes, not something that needs to be compiled WITH the outputted javascript. In c#/java you would not expect a referenced dll/jar to be compiled into your outputted dll/jar, its just a runtime dependency which is required.
I have tried having 2 separate reference files, one for core and one for ui, but as the ui will depend on core I get same problem, although at least this way the my-app.core.js is devoid of any ui related guff.
So is there a way to have references but NOT have them be outputted into the generated javascript files?
You can accomplish this by generating definition files for your TypeScript files:
tsc --declaration FileName.ts
In your build script do this for each TypeScript file and use the generated FileName.d.ts as the reference instead of FileName.ts
I had the following files:
-BusinessObjects
--Product.ts
--Customer.ts
--Order.ts
--BusinessObjects.d.ts
BusinessObjects.d.ts looks like this:
/// <reference path="Customer.d.ts" />
/// <reference path="Order.d.ts" />
/// <reference path="Product.d.ts" />
with Product, Customer, and Order each have a reference to BusinessObjects.d.ts
when I run:
tsc --out combine.js Customer.ts Order.ts
The output only references Customer and Order, Product is not included. When I referenced the *.ts files directly in my BusinessObjects.d.ts file however the combined output did include the unwanted file.

Categories

Resources