No mapping using babel and browserify in PlayFramework 2.5 - browserify

I am writing a project that is based on the seed at git#github.com:maximebourreau/play-reactjs-es6-seed.git
It appears to work with the 2.3 play framework, but I am getting errors using the 2.5 framework.
When I try to load a page, it get the following error:
RuntimeException: No mapping for /path/to/root/target/web/browserify/main.js
The full stack trace is at the bottom of the message.
This is the file that browserify is writing, but it seems that play does not know how to map it to a URL. I was not able to find anything that says where the file should be written or how to add a new mapping. I am also happy to use a plugin to do the translation.
Where should I be writing the file, or how should I tell play how to map the file?
My build.sbt is
name := """myProject"""
version := "1.0-SNAPSHOT"
lazy val root = (project in file(".")).enablePlugins(PlayJava)
scalaVersion := "2.11.7"
libraryDependencies ++= Seq(
javaJdbc,
cache,
javaWs,
)
val browserifyTask = taskKey[Seq[File]]("Run browserify")
val browserifyOutputDir = settingKey[File]("Browserify output directory")
browserifyOutputDir := target.value / "web" / "browserify"
browserifyTask := {
println("Running browserify");
val outputFile = browserifyOutputDir.value / "main.js"
browserifyOutputDir.value.mkdirs
"./node_modules/.bin/browserify -t [ babelify --presets [ es2015 react ] ] app/assets/javascripts/main.jsx -o "+outputFile.getPath !;
List(outputFile)
}
sourceGenerators in Assets <+= browserifyTask
resourceDirectories in Assets += browserifyOutputDir.value
My routes file is
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~
# An example controller showing a sample home page
GET / controllers.HomeController.index
# An example controller showing how to use dependency injection
GET /count controllers.CountController.count
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
POST /assessment controllers.AsyncController.assessment
#
# Play can't find files in node_modules in 2.5+
#GET /node_modules/*file controllers.NodeModulesController.at(file)
#GET /node_modules/*file controllers.NodeModulesController.at(file: String)
GET /node_modules/*file controllers.NodeModulesController.versioned(file: String)
#GET /node_modules/*file controllers.Assets.versioned(path="/node_modules", file:Asset)
The stack trace is
scala.sys.package$.error(package.scala:27)
sbt.Mapper$$anonfun$fail$1.apply(PathMapper.scala:37)
sbt.Mapper$$anonfun$fail$1.apply(PathMapper.scala:37)
sbt.Alternatives$$anon$1$$anonfun$$bar$1$$anonfun$apply$3.apply(PathMapper.scala:117)
sbt.Alternatives$$anon$1$$anonfun$$bar$1$$anonfun$apply$3.apply(PathMapper.scala:117)
scala.Option.orElse(Option.scala:257)
sbt.Alternatives$$anon$1$$anonfun$$bar$1.apply(PathMapper.scala:117)
sbt.Alternatives$$anon$1$$anonfun$$bar$1.apply(PathMapper.scala:117)
sbt.PathFinder$$anonfun$pair$1.apply(Path.scala:135)
sbt.PathFinder$$anonfun$pair$1.apply(Path.scala:135)
scala.collection.TraversableLike$$anonfun$flatMap$1.apply(TraversableLike.scala:251)
scala.collection.TraversableLike$$anonfun$flatMap$1.apply(TraversableLike.scala:251)
scala.collection.mutable.ResizableArray$class.foreach(ResizableArray.scala:59)
scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:47)
scala.collection.TraversableLike$class.flatMap(TraversableLike.scala:251)
scala.collection.AbstractTraversable.flatMap(Traversable.scala:105)
sbt.PathFinder.pair(Path.scala:135)
com.typesafe.sbt.jse.SbtJsTask$$anonfun$jsSourceFileTask$1$$anonfun$9$$anonfun$10$$anonfun$apply$4.apply(SbtJsTask.scala:292)
com.typesafe.sbt.jse.SbtJsTask$$anonfun$jsSourceFileTask$1$$anonfun$9$$anonfun$10$$anonfun$apply$4.apply(SbtJsTask.scala:286)
com.typesafe.sbt.web.SbtWeb$.withActorRefFactory(SbtWeb.scala:595)
com.typesafe.sbt.jse.SbtJsTask$$anonfun$jsSourceFileTask$1$$anonfun$9$$anonfun$10.apply(SbtJsTask.scala:285)
com.typesafe.sbt.jse.SbtJsTask$$anonfun$jsSourceFileTask$1$$anonfun$9$$anonfun$10.apply(SbtJsTask.scala:284)
scala.collection.immutable.Stream$$anonfun$map$1.apply(Stream.scala:376)
scala.collection.immutable.Stream$$anonfun$map$1.apply(Stream.scala:376)
scala.collection.immutable.Stream$Cons.tail(Stream.scala:1085)
scala.collection.immutable.Stream$Cons.tail(Stream.scala:1077)
scala.collection.immutable.Stream.foldLeft(Stream.scala:563)
scala.concurrent.Future$.sequence(Future.scala:491)
com.typesafe.sbt.jse.SbtJsTask$$anonfun$jsSourceFileTask$1$$anonfun$9.apply(SbtJsTask.scala:303)
com.typesafe.sbt.jse.SbtJsTask$$anonfun$jsSourceFileTask$1$$anonfun$9.apply(SbtJsTask.scala:272)
com.typesafe.sbt.web.incremental.package$.syncIncremental(package.scala:228)
com.typesafe.sbt.jse.SbtJsTask$$anonfun$jsSourceFileTask$1.apply(SbtJsTask.scala:271)
com.typesafe.sbt.jse.SbtJsTask$$anonfun$jsSourceFileTask$1.apply(SbtJsTask.scala:257)
scala.Function1$$anonfun$compose$1.apply(Function1.scala:47)
sbt.$tilde$greater$$anonfun$$u2219$1.apply(TypeFunctions.scala:40)
sbt.std.Transform$$anon$4.work(System.scala:63)
sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:228)
sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:228)
sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:17)
sbt.Execute.work(Execute.scala:237)
sbt.Execute$$anonfun$submit$1.apply(Execute.scala:228)
sbt.Execute$$anonfun$submit$1.apply(Execute.scala:228)
sbt.ConcurrentRestrictions$$anon$4$$anonfun$1.apply(ConcurrentRestrictions.scala:159)
sbt.CompletionService$$anon$2.call(CompletionService.scala:28)
java.util.concurrent.FutureTask.run(FutureTask.java:266)
java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
java.util.concurrent.FutureTask.run(FutureTask.java:266)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
java.lang.Thread.run(Thread.java:745)

You must change two lines in build.sbt
List(outputFile) to Nil
and
resourceDirectories in Asset += browserifyOutputDir.value
to
unmanagedResources in Assets += baseDirectory.value / "target/web/browserify/main.js"
For example:
browserifyTask := {
println("Running browserify");
val outputFile = browserifyOutputDir.value / "main.js"
browserifyOutputDir.value.mkdirs
"./node_modules/.bin/browserify -t [ babelify --presets [ es2015 react ] ] app/assets/javascripts/main.jsx -o "+outputFile.getPath !;
Nil
}
sourceGenerators in Assets <+= browserifyTask
unmanagedResources in Assets += baseDirectory.value / "target/web/browserify/main.js"

I ran accross the same error message, and almost same stacktrace,
after starting a project from the same git repo: git#github.com:maximebourreau/play-reactjs-es6-seed.git
The latest version of the repo is using play 2.5 and works fine, so
I don't think play version is a problem.
I noticed that the error was mentioning [sbt-jshint] somewhere,
and removing this line from project/plugins.sbt fixed the problem for me:
addSbtPlugin("com.typesafe.sbt" % "sbt-jshint" % "1.0.3")

Related

serve html file from node_modules using react native static server and webview

Is it possible to serve an html file from within the node_modules folder? I know how to serve from the assets folder, something like:
<WebView
...
source={{uri:'file:///android_asset/project_a/index.html'}}/>
or the corresponding path for iOS, but how can I access the node_modules folder and serve a file from there? I've tried using require() and/or using the path to the module I want served but with no luck.
The idea is that, I want to avoid copy-pasting build files between projects (i.e., from the build folder of project A to the assets/www of project B), instead, I want publish project A as an npm package and npm install and serve it from project B. This also improves version management of the projects.
For Android, you can run a custom Gradle task that able to will handle copy your assets in build time.
Create a Gradle file in your module's root, like below:
(I presume your module name is react-native-my-awesome-module and your HTML files is under www folder in module's root.)
awesome.gradle:
/**
* Register HTML asset source folder
*/
android.sourceSets.main.assets.srcDirs += file("$buildDir/intermediates/ReactNativeMyAwesomeModule")
/**
* Task to copy HTML files
*/
afterEvaluate {
def targetDir = "../../node_modules/react-native-my-awesome-module/www";
def fileNames = [ "*.html" ];
def htmlCopyTask = tasks.create(
name: "copyReactNativeMyAwesomeModuleAssets",
type: Copy) {
description = "copy react native my awesome module assets."
into "$buildDir/intermediates/ReactNativeMyAwesomeModule/www"
fileNames.each { fileName ->
from(targetDir) {
include(fileName)
}
}
}
android.applicationVariants.all { def variant ->
def targetName = variant.name.capitalize()
def generateAssetsTask = tasks.findByName("generate${targetName}Assets")
generateAssetsTask.dependsOn(htmlCopyTask)
}
}
After installing the module, put the below line in your project's android/app/build.gradle file:
apply from: file("../../node_modules/react-native-my-awesome-module/awesome.gradle");
When you build your app, your assets under www folder will be copied from your module. You can access your resources in your app like below :
<WebView
source={{uri: 'file:///android_asset/www/index.html'}}
/>
I ended up adding a script to packages.json that copies the files of interest into the assets/www folder before starting the app. Something like:
"scripts": {
"copy:build": "node -e \"fs.copyFileSync('./node_modules/dir-of-module','./assets/www/build')\"",
"start-android": "npm run copyAssets && react-native run-android",
...
}
If there is a better alternative, please let me know!

error building app with hermes: '..' is not recognized as an internal or external command

Building the react-native app on Windows 10.
The error is printed for line 165 of node_modules\react-native\react.gradle:
'..' is not recognized as an internal or external command, operable program or batch file
Line 165 is the 5th line in the following, starting with exec:
if (enableHermes) {
doLast {
def hermesFlags;
def hbcTempFile = file("${jsBundleFile}.hbc")
exec {
if (targetName.toLowerCase().contains("release")) {
// Can't use ?: since that will also substitute valid empty lists
hermesFlags = config.hermesFlagsRelease
if (hermesFlags == null) hermesFlags = ["-O", "-output-source-map"]
} else {
hermesFlags = config.hermesFlagsDebug
if (hermesFlags == null) hermesFlags = []
}
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
commandLine("cmd", "/c", getHermesCommand(), "-emit-binary", "-out", hbcTempFile, jsBundleFile, *hermesFlags)
} else {
commandLine(getHermesCommand(), "-emit-binary", "-out", hbcTempFile, jsBundleFile, *hermesFlags)
}
}
ant.move(
file: hbcTempFile,
toFile: jsBundleFile
);
if (hermesFlags.contains("-output-source-map")) {
ant.move(
// Hermes will generate a source map with this exact name
file: "${jsBundleFile}.hbc.map",
tofile: jsCompilerSourceMapFile
);
exec {
// TODO: set task dependencies for caching
// Set up the call to the compose-source-maps script
workingDir(reactRoot)
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
commandLine("cmd", "/c", *nodeExecutableAndArgs, composeSourceMapsPath, jsPackagerSourceMapFile, jsCompilerSourceMapFile, "-o", jsOutputSourceMapFile)
} else {
commandLine(*nodeExecutableAndArgs, composeSourceMapsPath, jsPackagerSourceMapFile, jsCompilerSourceMapFile, "-o", jsOutputSourceMapFile)
}
}
}
}
}
Here is the definition of hermescommand in android/app/build.gradle:
project.ext.react = [
enableHermes: true, // clean and rebuild if changing
// next added by Yossi
hermesCommand: "../../node_modules/hermesengine/win64-bin/hermes",
extraPackagerArgs: ["--sourcemap-output", "$buildDir/intermediates/assets/release/index.android.bundle.map"]
]
Which, as far as I understand, points to the following file:
node_modules\hermes-engine\win64-bin\hermes.exe
Any idea?
Updated definition in android/app/build.gradle (following the answer below):
project.ext.react = [
enableHermes: true, // clean and rebuild if changing
// -either- hermesCommand: "..\\..\\node_modules\\hermesengine\\win64-bin\\hermes",
// -or- hermesCommand: "../../node_modules/hermesengine/%OS-BIN%/hermes",
hermesCommand: "../../node_modules/hermesengine/%OS-BIN%/hermes",
extraPackagerArgs: ["--sourcemap-output", "$buildDir/intermediates/assets/release/index.android.bundle.map"]
]
The build output now, after the change, is the following:
> Task :app:bundleReleaseJsAndAssets
warning: the transform cache was reset.
Welcome to React Native!
Learn once, write anywhere
info Writing bundle output to:, C:\esites-grocery\test1\plumpclient\android\app\build\generated\assets\react\release\index.android.bundle
info Writing sourcemap output to:, C:\esites-grocery\test1\plumpclient\android\app\build/intermediates/assets/release/index.android.bundle.map
info Done writing bundle output
info Done writing sourcemap output
info Copying 141 asset files
info Done copying assets
The system cannot find the path specified.
> Task :app:bundleReleaseJsAndAssets FAILED
> Task :app:bundleReleaseJsAndAssets_SentryUpload_3001
Processing react-native sourcemaps for Sentry upload.
> Analyzing 2 sources
> Rewriting sources
> Adding source map references
Uploading sourcemaps for release com.plumpplum#RN62 Hermes+3001 distribution 3001
> Bundled 2 files for upload
> Uploaded release files to Sentry
> File upload complete
Source Map Upload Report
Minified Scripts
~/index.android.bundle (sourcemap at index.android.bundle.map)
Source Maps
~/index.android.bundle.map
> Task :app:bundleReleaseJsAndAssets_SentryUpload_1001
Processing react-native sourcemaps for Sentry upload.
> Analyzing 2 sources
> Rewriting sources
> Adding source map references
Uploading sourcemaps for release com.plumpplum#RN62 Hermes+1001 distribution 1001
> Bundled 2 files for upload
> Uploaded release files to Sentry
> File upload complete
Source Map Upload Report
Minified Scripts
~/index.android.bundle (sourcemap at index.android.bundle.map)
Source Maps
~/index.android.bundle.map
> Task :app:bundleReleaseJsAndAssets_SentryUpload_2001
Processing react-native sourcemaps for Sentry upload.
> Analyzing 2 sources
> Rewriting sources
> Adding source map references
Uploading sourcemaps for release com.plumpplum#RN62 Hermes+2001 distribution 2001
> Bundled 2 files for upload
> Uploaded release files to Sentry
> File upload complete
Source Map Upload Report
Minified Scripts
~/index.android.bundle (sourcemap at index.android.bundle.map)
Source Maps
~/index.android.bundle.map
> Task :app:bundleReleaseJsAndAssets_SentryUpload_4001
Processing react-native sourcemaps for Sentry upload.
> Analyzing 2 sources
> Rewriting sources
> Adding source map references
Uploading sourcemaps for release com.plumpplum#RN62 Hermes+4001 distribution 4001
> Bundled 2 files for upload
> Uploaded release files to Sentry
> File upload complete
Source Map Upload Report
Minified Scripts
~/index.android.bundle (sourcemap at index.android.bundle.map)
Source Maps
~/index.android.bundle.map
FAILURE: Build failed with an exception.
* Where:
Script 'C:\esites-grocery\test1\plumpclient\node_modules\react-native\react.gradle' line: 165
* What went wrong:
Execution failed for task ':app:bundleReleaseJsAndAssets'.
> Process 'command 'cmd'' finished with non-zero exit value 1
Here's more of react.gradle:
def getHermesCommand = {
// If the project specifies a Hermes command, don't second guess it.
if (!hermesCommand.contains("%OS-BIN%")) {
return hermesCommand
}
// Execution on Windows fails with / as separator
return hermesCommand
.replaceAll("%OS-BIN%", getHermesOSBin())
.replace('/' as char, File.separatorChar);
}
You are specifying ../../node_modules/hermesengine/win64-bin/hermes as the path, which is OS specific (as determined by not having an OS placeholder).
As per the comment, react.gradle therefore doesn't second guess your path and just passes it directly to the OS command interpreter.
This fails because your command interpreter does not allow / as directory separator.
Either specify a path that's valid for your system:
hermesCommand: "..\\..\\node_modules\\hermesengine\\win64-bin\\hermes",
Or specify an OS-independent path to let react.gradle transform it for each platform:
hermesCommand: "../../node_modules/hermesengine/%OS-BIN%/hermes",

How to add tailwindcss to KotlinJS

I am unable to add the tailwindcss library to my KotlinJS project. I tried multiple things.
I have multiple dependencies defined in my build.gradle.kts
implementation(npm("postcss", "latest"))
implementation(npm("postcss-loader", "latest"))
implementation(npm("tailwindcss", "1.8.10"))
I tried creating a tailwindcss.js in my webpack.config.d with this content
config.module.rules.push({
test: /\.css$/i,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
[
'tailwindcss'
],
],
},
},
}
]
}
);
But that doesn't do anything. I also tried modifying this with multiple options, but I was never able to get tailwindcss to compile. I also tried disabling and enabling the KotlinJS CSS support in build.gradle.kts
I can't find any info on how to add postcss to KotlinJS project.
Thank you for any help.
A basic integration can be achieved with the node-gradle plugin.
In your build.gradle.kts:
plugins {
id("com.github.node-gradle.node") version "3.0.0-rc2"
}
Also in build.gradle.kts define a task called "tailwindcss" that calls the tailwind CLI via npx. For example:
val tailwindCss = tasks.register<com.github.gradle.node.npm.task.NpxTask>("tailwindcss") {
// Output CSS location
val generatedFile = "build/resources/main/static/css/tailwind-generated.css"
// Location of the tailwind config file
val tailwindConfig = "css/tailwind.css"
command.set("tailwind")
args.set(listOf("build", tailwindConfig, "-o", generatedFile))
dependsOn(tasks.npmInstall)
// The location of the source files which Tailwind scans when running ```purgecss```
inputs.dir("src/main/kotlin/path/to/your/presentation/files")
inputs.file(tailwindConfig)
outputs.file(generatedFile)
}
Finally, in build.gradle.kts bind the task to your processResources step, so that it runs automatically. Note you may want to refine this later, because running tailwind every time the processResources step is invoked will slow down your dev cycle.
tasks.processResources {
dependsOn(tailwindCss)
}
Now we need a minimal package.json in the root of your project. For example:
{
"name": "MyProject",
"devDependencies": {
"tailwindcss": "^1.7.0"
}
}
Finally, we configure our tailwind config in the location defined by our NpxTask, in the example ```css/tailwind.css"
#tailwind base;
#tailwind components;
#tailwind utilities;
So now after the processResource step is run, gradle will invoke the Tailwind npx task, consume your source and write the CSS to the location you specified.
The accepted answer seems to not work anymore. Also, using the Node Gradle plugin is sub-optimal (KotlinJS already maintains its own package.json and yarn installation).
I managed to get Tailwind to work with KotlinJS thanks for this repository (GitHub) with a few small updates that you can find here (GitLab).
The linked I posted is the answer, the whole repository. It is not just a part of it
If you instead want me to copy/paste the whole repository instead here you're
= Kotlin/JS + Tailwind CSS =
This is a small sample repository to show the idiomatic way of
configuring these two systems together.
== Running it ==
. Run `./gradlew run`.
. Open `http://localhost:8080/` in your browser.
. 🎉 Notice we're using Tailwind CSS classes successfully.
== How To ==
Steps taken to make this work:
=== Dependencies ===
Add the following dependencies to your JS target (`jsMain` dependencies) in your Gradle file:
[source,kotlin]
----
implementation("org.jetbrains:kotlin-extensions:1.0.1-pre.148-kotlin-1.4.21")
implementation(npm("postcss", "8.2.6"))
implementation(npm("postcss-loader", "4.2.0")) // 5.0.0 seems not to work
implementation(npm("autoprefixer", "10.2.4"))
implementation(npm("tailwindcss", "2.0.3"))
----
* `kotlin-extensions` is necessary to get the JavaScript link:https://github.com/JetBrains/kotlin-wrappers/blob/master/kotlin-extensions/src/main/kotlin/kotlinext/js/CommonJS.kt#L20[`require`] function.
** Make sure the version number matches your version of the Kotlin multiplatform plugin at the top of your Gradle file.
** Kotlin Multiplatform 1.4.30 gave me `No descriptor found for library` errors. Try 1.4.21.
** Find the latest versions link:https://bintray.com/kotlin/kotlin-js-wrappers/kotlin-extensions[here].
* `postcss` and `autoprefixer` are link:https://tailwindcss.com/docs/installation#install-tailwind-via- npm[dependencies] as mentioned in the Tailwind CSS docs.
* `postcss-loader` is required because Kotlin/JS is built on top of Webpack.
** Note that while 5.0.0 is out, using it gave me build errors. The latest 4.x seems to work.
* `tailwindcss` is obviously what we're here for.
=== Add Tailwind as a PostCSS plugin ===
Just do link:https://tailwindcss.com/docs/installation#add-tailwind-as-a-post-css-plugin[this step].
If unsure, create this file in your project root:
[source,javascript]
----
// postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
}
}
----
=== Create your configuration file (optional) ===
link:https://tailwindcss.com/docs/installation#create-your-configuration-file[Official documentation].
Creating the `tailwind.config.js` file is a little tricky because simply `npx` won't work, as we haven't installed any
`node_modules`. Fortunately, Kotlin/JS has already done this for us.
Run the following:
[source,shell]
----
$ ./gradlew kotlinNpmInstall
$ ( cd build/js/ && npx tailwindcss init && mv tailwind.config.js ../../ )
----
This generates `tailwind.config.js` in the `build/js/` directory and then moves it up two directories to the project
root. Kotlin/JS generates the node modules into build/js/node_modules when the kotlinNpmInstall task runs.
This assumes your JavaScript module is `js`. If it's not, you'll need to change the `cd build/js/` part. If you're not
sure where your node_modules directory is, run find . -maxdepth 3 -name node_modules.
You should now have all your dependencies set up and config files created.
=== Create and Reference a Regular CSS File ===
_If you already have a CSS file that you're loading in your app, you can skip this step._
Create `app.css` in your `jsMain/resources/` directory. Put something obvious in there so you know
when it's loaded:
[source,css]
----
body {
background-color: red;
}
----
This file will get copied into the same folder as your transpiled JavaScript files.
In your JavaScript file (`client.kt` in this package), add:
[source,javascript]
----
kotlinext.js.require("./app.css")
----
to your main method. You can of course import the require method if you prefer.
If you run `./gradlew run`, you should be able to see a red page at `http://localhost:8080/`.
We're almost there, but we have two more steps: tell Webpack to use PostCSS and to finally inject Tailwind CSS.
=== Using PostCSS with Webpack ===
We want to "monkeypatch" the Webpack configuration that Kotlin/JS generates for us. This hook is
documented in the link:https://kotlinlang.org/docs/js-project-setup.html#webpack-bundling[webpack bundling] section. Basically, if we create .js files in webpack.config.d/, they'll be automatically
merged into build/js/packages/projectName/webpack.config.js, which exists after a build and you can go inspect.
The "problem", if you have `cssSupport.enabled = true` in your Gradle file (which you should!), is that this line
generates a webpack rule matching /\.css$/. We can't simply create another rule matching the same files...that
won't work.
So, we need to find the original rule and modify it. Create the following file relative to your project root:
[source,javascript]
----
// in webpack.config.d/postcss-loader.config.js
(() => {
const cssRule = config.module.rules.find(r => "test.css".match(r.test));
if (!cssRule) {
throw new Error("Could not resolve webpack rule matching .css files.");
}
cssRule.use.push({
loader: "postcss-loader",
options: {}
});
})();
----
We use an IIFE so that our new variable doesn't potentially interfere with other unseen variables.
Now PostCSS is working!
With PostCSS configured and the `tailwindcss` npm module in our dependencies, all that's left now
is to use it.
=== Importing Tailwind CSS ===
We're basically smooth sailing from here. Follow the link:https://tailwindcss.com/docs/installation#include-tailwind-in-your-css[Include Tailwind in your CSS] directions.
Just stick the following in your `app.css`:
[source,css]
----
#tailwind base;
#tailwind components;
#tailwind utilities;
----
If you start the server again, it should **Just Work**! It's a bit hard to tell, but if you check the devtools,
you should see the tw classes loading and massive js.js file being loaded (9.20mb!) which contains all of Tailwind CSS.
== Areas for Improvement ==
=== Modifications to app.css ===
Changes made to app.css don't get picked up unless you do a full `./gradlew clean` first, which is painful.
Adding the following line to build.gradle.kts seems to fix this:
[source,kotlin]
----
tasks.withType(KotlinWebpack::class.java).forEach { t ->
t.inputs.files(fileTree("src/jsMain/resources"))
}
----
=== Getting --continuous working ===
Even with the above fix, --continuous doesn't seem to work. 🤷
== Future Topics ==
* link:https://tailwindcss.com/docs/installation#building-for-production[Building for Production]

Rename img "src" dynamically in npm webpack

I am trying to create a module that will be used by other modules. However, this module contains html files.
Module 1 location c:/module_1
- Base HTML <img src="##__dirname/img/icon.png">
In module 1 he uses his icon.png.
In module 2, I would like it to use the icon.png of Module 2
I tried this:
plugins: [
new HtmlReplaceWebpackPlugin([
{
pattern: '##__dirname',
replacement: __dirname
},
...
html-replace-webpack-plugin
But compilation error occurs:
ERROR in Error: Child compilation failed:
Module not found: Error: Can't resolve './##_dirname/img/icon.png'
I noticed that it is possible to replace src after compiling
<img src="img/icon.png">
new HtmlReplaceWebpackPlugin([
{
pattern: /src=\"([^\"]*)\"/g,
replacement: function (match, $1)
{
return 'src="' + __dirname + '/src/img/' + $1 + '"';
}
}
]),
But I would like to replace before, and that when compiling it it takes the icon of the module in which it is being reused.
Is it possible to modify the img src dynamically in npm, even though it is a module that will be used as a dependency?
ps. I do not know much about the web and I do not know if I'm trying to do the project correctly. Just thought about reusing html code this way. If I'm doing something absurd, please let me know.
I solve the problem using React and this tutorial Setting Up a React.js Environment Using Npm, Babel 6 and Webpack

Preserve original sourcemap with Browserify

Suppose I have a module whose source code is not ECMA 5 (e.g. it's Coffescript or Typescript or whatever) and is distributed in compiled form with a source map. How can I include this source map in a Browserify bundle?
For example imagine a project with a single dependency:
index.js
node_modules
typescript_module
(main.ts)
dist
main.js
main.js.map
The "main.js.map" is not consumed by browserify. That is, the browserify bundle source map maps to "main.js" instead of deferring to the original map which describes "main.ts"
For most transforms, there is a way to input source maps generated by the prior step, but is there a way to just preserve them on the original input files, when source maps already exist?
This appears to be a bug/non-feauture of Browserify:
https://github.com/substack/node-browserify/issues/772
Answering my own question because it's very hard to track down any discussion of this issue with google and no mention of it in the Browserify docs.
My setup is the following:
tsc --project tsconfig.script.json --outDir ~temp/ts
Compiles src/script.ts to ~temp/ts/script.js and ~temp/ts/script.js.map.
browserify ~temp/ts/script.js --debug | exorcist --root ../../ ~temp/bfy/script.js.map > ~temp/bfy/script.js
Compiles ~temp/ts/script.js to ~temp/bfy/script.js and ~temp/bfy/script.js.map.
sorcery --input ~temp/bfy/script.js --output dist/script.js
Reads ~temp/bfy/script.js; finds ~temp/bfy/script.js.map + ~temp/ts/script.js.map, and finally outputs dist/script.js and dist/script.js.map.
The dist/script.js.map file does reference the original src/script.ts file.
Requires Browserify, Exorcist and Sorcery (and of course CoffeeScript or TypeScript or whatever).
If you are using a TypeScript library that you have control over (for example in a lerna monorepo), you can enable the following compilerOptions in tsconfig.json:
{
"compilerOptions": {
"sourceMap": false,
"inlineSourceMap": true,
"inlineSources": true,
// ...
}
}
browserify should now use and transform the inlined source maps.
browserify will not read source maps that reference another file, it will only use inlined source maps with inlined sources. I have written about this in the referenced issue on GitHub browserify/browserify#772.
Alternatively, if you do not have control over the source of the TypeScript library, but you would still like to see the original source in DevTools, you can use the sourceify library someone mentioned in another answer. However, I had to patch it to work and I submitted a pull request. It hasn't been merged yet (at the time of writing this). If you wish to test it yourself, you can install it directly from my branch:
npm install https://github.com/jeremija/sourceify#sources-content
Make sure to use the global transform -g [ sourceify ], because the default transform (-t) in Browserify does not modify files in node_modules.
Have a look at sourceify.
Just install it:
npm i --save-dev sourceify
... and add it as a transform to package.json:
"browserify": {
"transform": [
"sourceify"
]
}
... and it Just Works.
Try the following:
var gulp = require("gulp"),
browserify = require("browserify"),
tsify = require('tsify'),
source = require("vinyl-source-stream"),
sourcemaps = require("gulp-sourcemaps"),
buffer = require("vinyl-buffer"),
uglify = require("gulp-uglify"),
header = require("gulp-header");
var settings = {
projectName : "test"
};
gulp.task("bundle", function() {
var mainTsFilePath = "src/main.ts";
var outputFolder = "bundle/src/";
var outputFileName = settings.projectName + ".min.js";
var pkg = require("./package.json");
var banner = [
"/**",
" * <%= pkg.name %> v.<%= pkg.version %> - <%= pkg.description %>",
" * Copyright (c) 2015 <%= pkg.author %>",
" * <%= pkg.license %>",
" */", ""
].join("\n");
var bundler = browserify({
debug: true,
standalone : settings.projectName
});
// TS compiler options are in tsconfig.json file
return bundler.add(mainTsFilePath)
.plugin(tsify)
.bundle()
.pipe(source(outputFileName))
.pipe(buffer())
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(uglify())
.pipe(header(banner, { pkg : pkg } ))
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest(outputFolder));
});
Note: change the paths to match your project!