0

I am building a NativeScript application with Angular and I am trying to implement Theme switching, but I cannot get it to work with Webpack bundling.

My versions:

  • Angular 7.2.12
  • Nativescript-angular 7.2.3
  • Nativescript-themes 2.0.0
  • TypeScript 3.2.2

I followed the turtorials for implementing the feature in an Angular project: here and here. But these are for non-webpack (without the --bundle flag) builds. With the bundle flag (and the change described here) the switching no longer works and an error is thrown on each switch:

JS: ~/assets/themes/dark.scss
JS: Error: Css styling failed: Error: undefined:1:26: missing '{'

The theme file (located in ~/assets/themes/dark.scss)

ActionBar {
  background-color: #B20000;
  color: #FFFFFF;
}

.btn-primary {
  background-color: #B20000;
  color: #000000;
}

The function applyThemeCss() should extract the styling from the project, but it doesn't because of an error. The test project can be found here, on StackBlitz (I didn't use the Nativescript playground, since it doesn't have a package.json and the assets folder )

Mr.wiseguy
  • 4,092
  • 10
  • 35
  • 67
  • You can not update `package.json` directly but still you can install plugins in Playground, use the `+` (plus) icon at top left corner and choose `Add NPM Package`. And apply theme method expects a CSS file not SCSS, make sure your webpack picks it up at compile time. – Manoj Apr 08 '19 at 21:06
  • Hey @Manoj, thanks for your response! With the package.json i ment there is no place to add the script i am using (`tns run android --bundle`). Also, the nativescript-themes plugins docs have a scss file in there, are the docs wrong? (`var cssText = require('~/assets/themes/dark.scss');`) – Mr.wiseguy Apr 09 '19 at 06:09
  • I'm positive that SCSS may not work, but the file extension alone doesn't matter as long it's only CSS inside which is your case here. If you like to use SCSS syntax it should be pre-compiled to CSS. I doubt your `dark.scss` file is being not included in the bundle, but without a sample project to reproduce the issue can't be sure. – Manoj Apr 09 '19 at 09:08
  • @Manoj, I've added a project to the post that reproduces the problem – Mr.wiseguy Apr 09 '19 at 17:50

2 Answers2

1

applyThemeCss() expects CSS text not path to file. In the example code he uses a require statement to read the file then passing the CSS text to the method.

Also in your case if you want multiple themes to be applied dynamically, then you may have to modify your webpack.config.js to ship the CSS files to app bundle, something like below.

        // Copy assets to out dir. Add your own globs as needed.
        new CopyWebpackPlugin([
            { from: { glob: "assets/**" } },
            { from: { glob: "fonts/**" } },
            { from: { glob: "**/*.jpg" } },
            { from: { glob: "**/*.png" } },
        ], { ignore: [`${relative(appPath, appResourcesFullPath)}/**`] }),

Then use the applyTheme() method to pass the file name

Themes.applyTheme(ThemeService.THEME_PATH + theme);

If you like to use applyThemeCss() then read the file and pass the content

Themes.applyThemeCSS(knownFolders.currentApp().getFile('assets/themes/' + theme).readTextSync(), theme);
Manoj
  • 21,753
  • 3
  • 20
  • 41
  • I think this could work. But I still have the problem that my theme files are .scss files that are using scss variables. So they need to be compiled. I've tried playing arround with the https://www.npmjs.com/package/extract-text-webpack-plugin, but I couldn't get it to work with the regex `/\*\.theme\.scss$/` . Can you give me some help on this point? – Mr.wiseguy Apr 11 '19 at 19:19
  • You may not need an extra webpack plugin for this purpose. I think if you just modify / add entries for your scss file similar to `app.scss` in webpack config, it will be compiled and included in the bundle. Then at runtime just doing `require('path-to-your-scss')` should return the CSS text, just like in the example at plugin repo. – Manoj Apr 11 '19 at 19:52
  • Doing the above gives me the runtime error ERROR Error: Cannot find module '~/styles/themes/dark.scss', even if I include the files into the bundle via imports. So I think thats not it (updated the code in the repo) – Mr.wiseguy Apr 11 '19 at 20:05
  • Having the require statement in my code also throws a webpack warning: `Critical dependency: the request of a dependency is an expression`. So I don't think the require statement is the right solution here. It also makes sense the '~styles/' or '~assets/' doesn't work, since the whole project structure is different in Nativescript then in a normal webpack project ( run `webpack --env.android='android'` in the console of the github application to see the build output) – Mr.wiseguy Apr 11 '19 at 20:37
  • The require statement alone doesn't do anything. You have to update the webpack config too as I mentioned already. – Manoj Apr 11 '19 at 20:39
0

With the help of @Manoj, I managed to load my css themes into my application and switch themes.

The line { from: { glob: "assets/**" } }, copies the stylesheets over from 'assets/' to 'dist/assets'. But since I want to keep all my styling in the same folder ('styles/') I needed to update the code to: { from: { glob: "styles/themes/**" }, to: 'assets/' },.

And since I am using scss instead of css I still need to convert the styling. We can do this via the renderSync method of node-sass. (see this post for more information)

When we combine this we get the following code:

const scss = require('node-sass');

....

new CopyWebpackPlugin([
     {
        from: { glob: "styles/themes/*.scss" },
        to: 'assets/themes/[name].css',
        transform(content, path) {
            const result = sass.renderSync({ file: path });
            return result.css.toString();
        },
     },
     {from: {glob: "fonts/**"}},
     {from: {glob: "**/*.jpg"}},
     {from: {glob: "**/*.png"}},
], {ignore: [`${relative(appPath, appResourcesFullPath)}/**`]}),

This copies and compiles the theme files from styles/themes/ to assets/themes/. Note that this also ignores any subfolders located in the themes folder. This way we can do the following:

themes/
-- parts/ // <-- This folder will not be copied
---- _dark-variables.scss 
---- _light-variables.scss
-- dark.scss // <-- This will be compiled into assets/themes/dark.css
-- light.scss // <-- This will be compiled into assets/themes/light.css
Mr.wiseguy
  • 4,092
  • 10
  • 35
  • 67