diff --git a/flow-server/src/main/java/com/vaadin/flow/server/frontend/NodeUpdater.java b/flow-server/src/main/java/com/vaadin/flow/server/frontend/NodeUpdater.java index b3d7b336030..4a1d6a70402 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/frontend/NodeUpdater.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/frontend/NodeUpdater.java @@ -263,6 +263,10 @@ static Map getDefaultDevDependencies() { defaults.put("typescript", "4.0.3"); defaults.put("ts-loader", "8.0.12"); + defaults.put("file-loader", "6.1.0"); + defaults.put("extract-loader", "5.1.0"); + defaults.put("lit-css-loader", "0.0.4"); + // Constructable style sheets is only implemented for chrome, // polyfill needed for FireFox et.al. at the moment defaults.put("construct-style-sheets-polyfill", "2.4.2"); diff --git a/flow-server/src/main/resources/plugins/application-theme-plugin/application-theme-plugin.js b/flow-server/src/main/resources/plugins/application-theme-plugin/application-theme-plugin.js index 75ae11e582c..67e373706a2 100644 --- a/flow-server/src/main/resources/plugins/application-theme-plugin/application-theme-plugin.js +++ b/flow-server/src/main/resources/plugins/application-theme-plugin/application-theme-plugin.js @@ -17,7 +17,7 @@ const fs = require('fs'); const path = require('path'); const generateThemeFile = require('./theme-generator'); -const { copyThemeResources, copyStaticAssets } = require('./theme-copy'); +const copyStaticAssets = require('./theme-copy'); let logger; @@ -112,7 +112,6 @@ function handleThemes(themeName, themesFolder, projectStaticAssetsOutputFolder) const themeProperties = getThemeProperties(themeFolder); - copyThemeResources(themeFolder, projectStaticAssetsOutputFolder); copyStaticAssets(themeProperties, projectStaticAssetsOutputFolder, logger); const themeFile = generateThemeFile(themeFolder, themeName); diff --git a/flow-server/src/main/resources/plugins/application-theme-plugin/package.json b/flow-server/src/main/resources/plugins/application-theme-plugin/package.json index 2027a6bd109..079d7ab58a4 100644 --- a/flow-server/src/main/resources/plugins/application-theme-plugin/package.json +++ b/flow-server/src/main/resources/plugins/application-theme-plugin/package.json @@ -6,7 +6,7 @@ ], "repository": "vaadin/flow", "name": "@vaadin/application-theme-plugin", - "version": "0.1.6", + "version": "0.2.0", "main": "application-theme-plugin.js", "author": "Vaadin Ltd", "license": "Apache-2.0", diff --git a/flow-server/src/main/resources/plugins/application-theme-plugin/theme-copy.js b/flow-server/src/main/resources/plugins/application-theme-plugin/theme-copy.js index e3eaeaaf70b..34abd768b2d 100644 --- a/flow-server/src/main/resources/plugins/application-theme-plugin/theme-copy.js +++ b/flow-server/src/main/resources/plugins/application-theme-plugin/theme-copy.js @@ -15,52 +15,13 @@ */ /** - * This file handles copying of theme files to - * [staticResourcesFolder] + * This contains functions and features used to copy theme files. */ const fs = require('fs'); const path = require('path'); const glob = require('glob'); -/** - * Copy theme files to static assets folder. All files in the theme folder will be copied excluding - * css, js and json files that will be handled by webpack and not be shared as static files. - * - * @param {string} themeFolder Folder with theme file - * @param {string} projectStaticAssetsOutputFolder resources output folder - */ -function copyThemeResources(themeFolder, projectStaticAssetsOutputFolder) { - if (!fs.existsSync(path.resolve(projectStaticAssetsOutputFolder))) { - require('mkdirp')(path.resolve(projectStaticAssetsOutputFolder)); - } - copyThemeFiles(themeFolder, projectStaticAssetsOutputFolder); -} - -const ignoredFileExtensions = [".css", ".js", ".json"]; - -/** - * Recursively copy files found in theme folder excluding any with a extension found in the `ignoredFileExtensions` array. - * - * Any folders met will be generated and the contents copied. - * - * @param {string} folderToCopy folder to copy files from - * @param {string} targetFolder folder to copy files to - */ -function copyThemeFiles(folderToCopy, targetFolder) { - fs.readdirSync(folderToCopy).forEach(file => { - if (fs.statSync(path.resolve(folderToCopy, file)).isDirectory()) { - if (!fs.existsSync(path.resolve(targetFolder, file))) { - fs.mkdirSync(path.resolve(targetFolder, file)); - } - copyThemeFiles(path.resolve(folderToCopy, file), path.resolve(targetFolder, file)); - } else if (!ignoredFileExtensions.includes(path.extname(file))) { - fs.copyFileSync(path.resolve(folderToCopy, file), path.resolve(targetFolder, file)); - } - }); -} - - /** * Copy any static node_modules assets marked in theme.json to * project static assets folder. @@ -118,4 +79,4 @@ function copyStaticAssets(themeProperties, projectStaticAssetsOutputFolder, logg }); }; -module.exports = { copyThemeResources, copyStaticAssets }; +module.exports = copyStaticAssets; diff --git a/flow-server/src/main/resources/plugins/theme-loader/package.json b/flow-server/src/main/resources/plugins/theme-loader/package.json index db02fde7259..171891789f4 100644 --- a/flow-server/src/main/resources/plugins/theme-loader/package.json +++ b/flow-server/src/main/resources/plugins/theme-loader/package.json @@ -6,7 +6,7 @@ ], "repository": "vaadin/flow", "name": "@vaadin/theme-loader", - "version": "0.0.2", + "version": "0.0.3", "main": "theme-loader.js", "author": "Vaadin Ltd", "license": "Apache-2.0", diff --git a/flow-server/src/main/resources/plugins/theme-loader/theme-loader.js b/flow-server/src/main/resources/plugins/theme-loader/theme-loader.js index 3e9f03a1412..353e4e23c4e 100644 --- a/flow-server/src/main/resources/plugins/theme-loader/theme-loader.js +++ b/flow-server/src/main/resources/plugins/theme-loader/theme-loader.js @@ -2,7 +2,7 @@ const loaderUtils = require("loader-utils"); const fs = require('fs'); const path = require('path'); -// Collect groups [url(] [ |'|"]optional './', file part and end of url +// Collect groups [url(] [ |'|"]optional './|../', file part and end of url const urlMatcher = /(url\()(\'|\")?(\.\/|\.\.\/)(\S*)(\2\))/g; /** @@ -21,11 +21,11 @@ module.exports = function (source, map) { let themeFolder = handledResourceFolder; // Recurse up until we find the theme folder or don't have 'theme' on the path. while (themeFolder.indexOf("theme") > 1 - && path.basename(path.resolve(themeFolder, "..")) !== "theme") { + && path.basename(path.resolve(themeFolder, "..")) !== "theme") { themeFolder = path.resolve(themeFolder, ".."); } // If we have found no theme folder return without doing anything. - if(path.basename(path.resolve(themeFolder, "..")) !== "theme") { + if (path.basename(path.resolve(themeFolder, "..")) !== "theme") { this.callback(null, source, map); return; } @@ -35,14 +35,15 @@ module.exports = function (source, map) { source = source.replace(urlMatcher, function (match, url, quoteMark, replace, fileUrl, endString) { let absolutePath = path.resolve(handledResourceFolder, replace, fileUrl); if (fs.existsSync(absolutePath) && absolutePath.startsWith(themeFolder)) { - logger.debug("Updating url for file '", replace, fileUrl, "' to use 'VAADIN/static'"); + const frontendThemeFolder = "theme/" + path.basename(themeFolder); + logger.debug("Updating url for file", "'" + replace + fileUrl + "'", "to use", "'" + frontendThemeFolder + "/" + fileUrl + "'"); const pathResolved = absolutePath.substring(themeFolder.length).replace(/\\/g, '/'); - // keep the url the same except replace the ./ to VAADIN/static + // keep the url the same except replace the ./ or ../ to theme/[themeFolder] if (quoteMark) { - return url + quoteMark + 'VAADIN/static' + pathResolved + endString; + return url + quoteMark + frontendThemeFolder + pathResolved + endString; } - return url + 'VAADIN/static' + pathResolved + endString; + return url + frontendThemeFolder + pathResolved + endString; } else if (options.devMode) { logger.info("No rewrite for '", match, "' as the file was not found."); } diff --git a/flow-server/src/main/resources/webpack.generated.js b/flow-server/src/main/resources/webpack.generated.js index 777cd809c57..6c17b897bf2 100644 --- a/flow-server/src/main/resources/webpack.generated.js +++ b/flow-server/src/main/resources/webpack.generated.js @@ -16,6 +16,10 @@ const ApplicationThemePlugin = require('@vaadin/application-theme-plugin'); const path = require('path'); const baseDir = path.resolve(__dirname); // the folder of app resources (main.js and flow templates) + +// this matches /theme/my-theme/ and is used to check css url handling and file path build. +const themePartRegex = /(\\|\/)theme\1[\s\S]*?\1/; + const frontendFolder = '[to-be-generated-by-flow]'; const fileNameOfTheFlowGeneratedMainEntryPoint = '[to-be-generated-by-flow]'; @@ -160,7 +164,9 @@ module.exports = { options: { url: (url, resourcePath) => { // Only translate files from node_modules - return resourcePath.includes('/node_modules/'); + const resolve = resourcePath.match(/(\\|\/)node_modules\1/); + const themeResource = resourcePath.match(themePartRegex) && url.match(/^theme\/[\s\S]*?\//); + return resolve || themeResource; }, // use theme-loader to also handle any imports in css files importLoaders: 1 @@ -176,6 +182,23 @@ module.exports = { } ], }, + { + // File-loader only copies files used as imports in .js files or handled by css-loader + test: /\.(png|gif|jpg|jpeg|svg|eot|woff|woff2|ttf)$/, + use: [{ + loader: 'file-loader', + options: { + outputPath: 'static/', + name(resourcePath, resourceQuery) { + const urlResource = resourcePath.substring(frontendFolder.length); + if(urlResource.match(themePartRegex)){ + return /^(\\|\/)theme\1[\s\S]*?\1(.*)/.exec(urlResource)[2]; + } + return '[path][name].[ext]'; + } + } + }], + }, ] }, performance: {