How to integrate Framework 7 with shadow-cljs and clojurescript - clojurescript

When I try to initialize Framework7 and Framework7-React within a clojurescript project, using shadow-cljs I get errors like this:
f7.js:31 Uncaught TypeError: Framework7 is not a constructor
at Object.init (f7.js:31)
at F7App.value (app.js:162)
at commitLayoutEffects (react-dom.development.js:21965)
at HTMLUnknownElement.callCallback (react-dom.development.js:363)
at Object.invokeGuardedCallbackImpl (react-dom.development.js:412)
at invokeGuardedCallback (react-dom.development.js:467)
at commitRootImpl (react-dom.development.js:25025)
at exports.unstable_runWithPriority (scheduler.development.js:816)
at runWithPriority$2 (react-dom.development.js:12189)
at commitRoot (react-dom.development.js:24866)
The Framework7 initialization normally consists of nothing more than the following
// Import Framework7
import Framework7 from 'framework7/framework7-lite.esm.bundle.js';
// Import Framework7-React Plugin
import Framework7React from 'framework7-react';
// Import Framework7 Styles
import 'framework7/css/framework7.bundle.css';
// Init F7 React Plugin
Framework7.use(Framework7React);
There is a hacky solution, the involves two separate build pipelines in order to get
Framework7 up and running in a simple shadow-cljs sample project.
I created a a test project that shows both versions, the non-working clojurescript version, where the error occurs can be run by:
npx shadow-cljs watch :direct-no-webpack
It uses the client2.cljs
The working version initializes Framework7 using the ES6 import syntax, and is transpiled using webpack.
yarn run build-dev
npx shadow-cljs watch :direct
It produces the above mentioned error.
I do not understand in detail, what exactly is going on in Framework7 as multiple wrappers are involved.
How can I correctly import and use Framework7 with Clojurescript and Shadow-CLJS?

I don't know anything about Framework7 but it is usually best to just follow the most basic instructions. In this case I added no additional build config and just required the packages directly.
(ns foo.bar
(:require
["framework7" :as f7]
["framework7-react" :as f7r]))
;; avoiding top-level side-effects because of hot-reload
(defn init []
(.use f7 f7r))
I can't tell if this actually works but it didn't throw an error. Avoid accessing ".esm.js" files or so directly since may cause conflicts if you access those but "framework7-react" accesses different ones. Mixing CommonJS and ESM files is not well supported at the moment.
.css imports are not supported by shadow-cljs.

Even though I read the shadow-clj guidelines on how to use npm packages. I had to use the direct path to the Javascript-files in the node_modules directory.
The correct imports are:
(ns app.ui.client2
(:require
["framework7/framework7-lite.esm.bundle.js" :default Framework7]
["framework7-react/framework7-react.esm.js" :default Framework7React]))
I modifed the test project mentioned in my question.

Related

How to add shadow cljs watcher on js directory in a re-frame app?

I have created a basic re-frame app using the template lein new re-frame my-proj. This particular project is interfacing with a framework (ecsy) that requires some ES6 modules and ES6 classes e.g code that is generated by the user, not simply called from cljs. Since Clojurescript does not currently generate ES6 code, I have created some wrapper ES6 modules in my project from which I plan to call into cljs code.
After much futzing, I have discovered that it's not necessary to turn these js wrapper modules into full-blown npm modules under 'node_modules', but rather I can simply put them in a sub-directory of my project e.g resources/libs and then add this directory to :js-options in shadow-cljs.edn:
{:lein true
:nrepl {:port 8777}
:builds {:app {:target :browser
:output-dir "resources/public/js/compiled"
:asset-path "/js/compiled"
:modules {:app {:init-fn re-pure-ecs-simple.core/init
:preloads [devtools.preload]}}
:devtools {:http-root "resources/public"
:http-port 8280}
;;add this
:js-options {:js-package-dirs ["node_modules" "resources/libs"]}}}}
So everything works fine now, but the only problem is if I edit any of the js files in 'resources/public' the lein.bat dev compiler doesn't detect the changes. I can go in and make a mock change to a '.cljs' file, which does cause the compiler to re-compile, but it still doesn't pick up on the changes made to the '.js' file (or '.mjs' file). I have to kill, via ctrl-c, the compiler and re-start it to get the change propagated. Unfortunately, this takes about 15 seconds to compile since it's a full compile.
I tried adding 'resources/libs' to my 'project.clj':
:source-paths ["src/clj" "src/cljs" "resources/libs"]
to no avail.
I also tried deleting the compiled js files from <my_proj-dir>/resources/public/js/compiled/cljs-runtime:
rm 'module$node_modules$systems.js' 'module$node_modules$systems.js.map'
In this case, the compiler does re-generate the files (upon doing a mock .cljs change), but it still uses the prior version e.g. it must be using a cached version.
Is there a way I can add a watcher to this js directory so I can do incremental builds? There's obviously a watcher on the 'src/cljs' directory already. I have consulted the shadow-cljs user gd. but honestly, it's a little overwhelming.
You can follow the directions for requiring local .js in the User's Guide.
Basically you put the .js files into the same folder as your .cljs file and then just require it via (:require ["./foo.js" :as foo]). No additional config required.

Import Polymer 2 components in Polymer 3

I am developing a web component using Polymer v3, and need to include some custom elements defined in legacy Polymer 2 components in the template HTML of my new component.
Since HTML imports are no longer supported in Polymer 3, what approach should I take to include them? If I was using Polymer 2 I could just add the following in my component's HTML file:
<link rel="import" href="../my-legacy-component.html">
I have tried adding the above link into the template HTML of my component, but it appears that doesn't work. I have also tried various import commands to reference the JS files inside the legacy component directly, but received various inscrutable JS errors so I'm not sure if that is the correct way to go either.
I can't believe there isn't a simple way to do this - would the Polymer team really introduce a new version of the library that is completely incompatible with all the components created using older versions?
Did you try to use polymer-modulizer?
Modulizer performs many different upgrade tasks, like:
Detects which .html files are used as HTML Imports and moves them to .js
Rewrites in HTML to import in JS.
Removes "module wrappers" - IIFEs that scopes your code.
Converts bower.json to package.json, using the corresponding packages on npm.
Converts "namespace references" to the proper JS module import, ie: Polymer.Async.timeOut to timeOut as imported from #polymer/polymer/lib/util/async.
Creates exports for values assigned to namespace referencs. ie, Foo.bar = {...} becomes export const bar = {...}
Rewrites namespace objects - an object with many members intended to be used as a module-like object, to JS modules.
Moves Polymer element templates from HTML into a JS template string.
Removes s if they only contained a template.
Moves other generic HTML in the document into a JS string and creates it when the module runs.
more on github
I have ran into the same problem with the module js-yaml earlier. I don't have enough reputation for a comment yet so I just write it down here.
Run this sudo npm install -g js-yaml -> This will install the missing package for the tool
Then at the root of your project, run modulizer --import-style name --out . -> This will convert your component from Polymer 2 to Polymer 3. The option --import-style name tells the tool to use package name instead of path. --out will make the tool writes those files to the directory.
After that, if no error prompts. Try to serve it with polymer serve --module-resolution=node -> Since we are using node modules now, we have to provide the --module-resolution=node option.

Use a different React version with clojurescript react libraries (reagent,om,rum,quiescent)

How can I use a different React version with Reagent, Om, Rum, Quiescent or Brutha?
Self answer since this is asked frequently:
First you have to tell Leiningen to exclude the cljsjs/react dependencies:
[rum "0.6.0" :exclusions [[cljsjs/react] [cljsjs/react-dom]]]
If you have other dependencies pulling in cljsjs/react you can use a global exclusion:
:exclusions [[cljsjs/react] [cljsjs/react-dom]]
Next you have to satisfy the compiler since it won't find the namespaces cljsjs.react and cljsjs.react.dom. For this create two files that hold these namespaces in your source directory. For instance
- src/cljsjs/react.cljs
- src/cljsjs/react/dom.cljs
Both only need the namespace declaration and can otherwise be empty (ns cljsjs.react).
Now you can include any React version you'd like manually in your HTML file with a normal <script> tag.
Alternative:
You can also use foreign-libs compiler option.

ES6 imports: can we make this fail? import React, {BadKey} from "react";

I'd like my imports to be more fail-fast. I want my build process to fail if someone tries to import something that does not exist.
For default imports it seems to work fine, as the following will fail:
import Something from "doesNotExist";
But if I import an attribute of an existing module, it does not fail:
import React, {BadKey} from "react";
How can I make it fail by default?
I'm using Webpack / Babel5 / NPM 2.14
Use a strongly-typed language like Typescript, which can error in this condition. We actually moved to eliminate all our default exports and imports, because the name checking available for import { Thing } is super helpful.
If you're already using Webpack, eslint-loader is one way to integrate with ESLint as part of your build process.
Webpack can be a little cryptic with errors during module builds, though, so take note of the NoErrorsPlugin at the bottom of the README.
Also, consider using Webpack's bail flag (set to true) to abandon building as soon as module errors are encountered. IIRC the default Webpack behavior just omits the error-ing module from the emitted bundle with a note in the console, which will result in a runtime error anyway (module missing).

atom packages, clojurescript, google closure and dependency management

I'm writing some atom (the editor) package with ClojureScript. And i faced dependency load issue.
When compiled ClojureScript produces file like this (main.js):
goog.addDependency("base.js", ['goog'], []);
goog.addDependency("../cljs/core.js", ['cljs.core'], ...)
goog.addDependency("../clojure/browser/event.js", ...)
Obviously, ClojureScript heavily depends on Google Closure dependency management.
But, to be able to use Google Closure i need to include goog/base.js file.
The only way that i found is to add to goog/base.js:
module.exports = goog
and add to main.js:
require('./goog/base.js')
This is very bad approach, because these files are generated - so they can be overridden.
Also, release compilation will not include these lines.
The question is how can i use both these dependency systems?
Or is it possible to use ClojureScript w/o Google Closure?
Please advice, thanks!
If you set your ClojureScript :optimizations to something other than :none (e.g., :whitespace) then the resulting .js file will include the Google Closure code inlined and you won't have to reference it separately.
(Note that this means you might not be able to use a main function in your ClojureScript code, but you can just put a call to your main function somewhere at the toplevel.)