Getting path to file in published webcomponent - javascript

I have a webcomponent where I need to add a link tag to the head and set the href equal to a folder inside that node module.
Right now I'm in the building phase of this component where my structure look like this:
So I need to add the fontawsome.css script to the head of my page. I've created the following script:
constructor() {
super();
if (!this.isFontAwesomeLoaded()) {
this.iclass = '';
const fontEl = document.createElement('link');
fontEl.rel = 'stylesheet';
fontEl.href = "./fontawesome/css/all.css";
document.head.appendChild(fontEl);
}
}
Now there is a problem with this the path ./fontawesome/css/all.css won't work when it hits the head tag because the index.html file that attempts to load it doesn't have the folder in its project. instead when it hits product it needs to find the absolute path to my module and then to the fontawesome folder.
My question is how can I get that path?

There is a lot of things that should be avoided:
External dependencies from within web-component is a bad idea, especially something big like fontawesome. You said that you try to avoid coupling, but actually you introduce it with such dependency.
Web-components rarely should be used without shadow-dom (IMHO), by accepting fontawesome you basically ignore that functionality of web-components.
There are a couple of ways you could handle such scenario:
extract what you need from fontawesome's all.css into your web-component.
state in your component's docs that it depends on fontawesome and that the client should provide it on their side
use CDN (really bad decision, avoid this):
constructor() {
super();
if (!this.isFontAwesomeLoaded()) {
this.iclass = '';
const fontEl = document.createElement('link');
fontEl.rel = 'stylesheet';
fontEl.href = "https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css";
document.head.appendChild(fontEl);
}
}

Related

How to add custom scripts bundle in NextJS

I have some legacy custom javascripts that I need to bundle and put them in _document.js as a link. The filename should include a hash.
What would be the best way to accomplish this?
I tried webpack configs regarding entry/output but they break NextJs build.
The problem is that we use things like window, document, etc that do crash in server side.
Ideally what is needed is to inject this into a tag, as compiled / babelified javascript code.
What I tried is
Webpack HTML Plugin plus other plugins like InlineChunk or
InlineSource plugins. They didn't work because they generate code in
an index.html that is not used by NextJS.
Using Raw Loader to get the file content. Doesn't work because it is
not babelified.
Adding a custom entry to the Webpack config, like scripts:
'path/to/my-entry.js'. Didn't work because it adds a hash name to the
file and I have no way of knowing it.
Adding a custom entry into the NextJs polyfills. I thought it made
sense, but the polyfill tag has a nomodule which prevents its code to
run on new browsers.
Another options is to add the javascript code as a string, and then using __dangerouslySetInnerHtml but the problem is that I lose linter and babel abilities there.
I tried adding it as a page, but crashes for local development and even on build
webpack.config.js
module.exports = (nextConfig = {}) =>
Object.assign({}, nextConfig, {
webpack(config, options) {
const nextJsEntries = config.entry;
config.entry = async () => {
const entries = await nextJsEntries();
entries['pages/rscripts'] = 'test/test.js';
return entries;
};
...
Then in _document.js
<script src={`${publicRuntimeConfig.ASSET_PREFIX}/_next/${this.props.buildManifest.pages['/rscripts'][2]}`} />
You can just import js file like import 'path/to/js_file' in your _app.js/app.tsx file
import "../styles/globals.css"
import "../js/test"
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
This one works fine for me
I wanted to add another answer here as I came across this and I believe some things have changed in Next JS. Next now has this script component that you can use to load external scripts or dangerously set a script.
The Next.js Script component, next/script, is an extension of the HTML
element. It enables developers to set the loading priority of
third-party scripts anywhere in their application, outside next/head,
saving developer time while improving loading performance.
The cool thing is you can put them into whatever pages you want, maybe you have a script you want on a homepage, but not other pages, and Next will extract them and place them on the page based on the strategy you select. There are a few gotchas, can't load in the head, beforeInteractive is a little finicky, so I would read the link above and the actual API reference before making any choices.
import { useEffect } from 'react';
import Script from 'next/script';
function thirdPartyScript() {
useEffect(() => {
// just for fun. This actually fires
// before the onLoad callback
}, []);
return (
<Script
id="test-script"
strategy="afterInteractive"
src="/public/pages/scripts/test.js"
onLoad={() => {
console.log('Onload fires as you would expect');
}}
/>
);
}

How to load external library from local path

I have a code importing list of external urls(http//....js)
for (const id in urls) {
let tag = document.createElement('script');
tag.async = false;
tag.src = urls[id];
let body = document.getElementsByTagName('body')[0];
body.appendChild(tag);
}
But for security reasons, I copy&pasted the js file to local file, but dont know how to import it like I used to do.
Simply
import "../lib/asmcrypto.js";
gives me thousands of errors saying 'Expected an assignment or function call and instead saw an expression'.
Any help is appreciated!
If you're inside a browser, the most common way of loading your JS resource would be:
<script src="/lib/asmcrypto.js"></script>
If you're using Webpack at some level, like when you're project is on React or Angular or ...
import crypto from "../lib/asmcrypto.js";
It's important to understand that, it's usually the bundler e.g. Webpack that takes care of import and export in your project.

How to import/load entire file to include in my bundle?

How to include an entire file into my bundle main.js?
ES6 can import/export functions and classes. But what if i want to include the whole content from another file into my bundle main.js? how to do it?
I came across the query on Stackoverflow: Managing jQuery plugin dependency in webpack.
I'm not sure about this question though. Those options given there seem to target injecting implicit globals, configuring this, disabling AMD, to include large dists. I don't think this is what i want.
Let's say i have two files in src directory
1- rough.js
const rgh = "qwerty"
2- index.js
import './rough.js' //something like this
console.log (rgh)
Now what i expect in bundle.js is
const rgh = "query";
console.log(rgh)
I just want all the content inside one of my file to get all transported to index.js for webpack to bundle them
Those options given there seem to target injecting implicit globals,
configuring this, disabling AMD, to include large dists. I don't think
this is what i want.
To understand this you need to understand what webpack is doing for you. Web pack takes a series of Javascript files (and more importantly their contents) and parses these into one file. That's what it does from a file point of view, but if you ignore the file and think about what it does from a code point of view, it takes each one of the imported objects and makes them available to other objects depending upon the rules you define in your code (using import and export). You can think of this from a closure point of view something like this:
if you have some code like:
import a from 'a.js';
export default b(){
console.log(a.test());
}
This will be turned into something like, in one js file:
var a = (function() {
var testStr = "test";
function test(){
return testStr;
}
return {test:test};
})();
var b = (function(a) {
console.log(a.test());
})(a);
So you can see that the file isn't really important. What's important is the scope. b can use a because it is injected into it's scope (In this instance as a IIFE).
In the above example a and b are in the global scope but testStr isn't.
So when your talking about "importing my file", you need to forget about that and think about what objects in that file you want to import how. Any variables "in that file" declared directly var a = ....; are in the global scope. So it sounds like what you want to do is import the objects in that file into the global scope.
you just need to import that file in main.js
like this way

How to import a module from the static using dynamic import of es6?

I'm trying to add dynamic import into my code to have a better performance on the client-side. So I have a webpack config where is bundling js files. On SFCC the bundled files are in the static folder where the path to that files is something like this: /en/v1569517927607/js/app.js)
I have a function where I'm using dynamic import of es6 to call a module when the user clicks on a button. The problem is that when we call for that module, the browser doesn't find it because the path is wrong.
/en/lazyLoad.js net::ERR_ABORTED 404 (Not Found)
This is normal because the file is on /en/v1569517927607/js/lazyLoad.js.
There is a way to get it from the right path? Here is my code.
window.onload = () => {
const lazyAlertBtn = document.querySelector("#lazyLoad");
lazyAlertBtn.addEventListener("click", () => {
import(/* webpackChunkName: "lazyLoad" */ '../modules/lazyLoad').then(module => {
module.lazyLoad();
});
});
};
I had the same problem and solved it using the Merchant Tools > SEO > Dynamic Mapping module in Business Manager.
There you can use a rule like the following to redirect the request to the static folder:
**/*.bundle.js i s,,,,,/js/{0}.bundle.js
All my chunk files are named with the <module>.bundle pattern.
Here you can find more info :
https://documentation.b2c.commercecloud.salesforce.com/DOC1/topic/com.demandware.dochelp/content/b2c_commerce/topics/search_engine_optimization/b2c_dynamic_mappings.html
Hope this helps.
I believe you'll likely need to do some path.resolve() magic in either your import statement or your webpack.config.js file as is shown in the accepted answer to this question: Set correct path to lazy-load component using Webpack - ES6
We did it in a different way. That required two steps
From within the template file add a script tag that creates a global variable for the static path. Something like
// inside .isml template
<script>
// help webpack know about the path of js scripts -> used for lazy loading
window.__staticPath__ = "${URLUtils.httpsStatic('/')}";
</script>
Then you need to instruct webpack to know where to find chunks by changing __webpack_public_path__ at runtime
// somewhere in your main .js file
// eslint-disable-next-line
__webpack_public_path__ = window.__staticPath__ + 'js/';
Optional step:
You might also want to remove code version from your __staticPath__ using replace (at least we had to do that)
__webpack_public_path__ = window.__staticPath__.replace('{YOUR_CODE_VERSION_GOES_HERE}', '') + 'js/';

Can't require custom module from renderer in Electron

I'm trying to make some classes that I can import for use in my project, but I'm having some trouble actually importing the modules I'm making.
My File Structure looks like this:
├╴main.js
└╴src/
├╴html/
│ └╴index.html
├╴css/
│ └╴index.css
└╴js/
├╴index.js
└╴participant.js
All the index.* files are related to each other, hence having the same name.
The trouble-makers in question are index.js, my renderer for index.html, and participant.js
Here's the code I got:
// index.js
const {Participant} = require("./participant");
const addNodeBtn = document.getElementById('add-node');
addNodeBtn.addEventListener('click', () => {
// this is really just filler code to see if everything works
let p = new Participant("Jason");
alert(`His name was ${p.name}`);
});
and
// participant.js
"use strict";
var Participant = class {
constructor(name) {
this.name = name;
}
}
module.exports.Participant = Participant;
For whatever reason I keep getting the error "cannot find module ./participant".
Here's the alternatives I've tried:
require("participant");
require("./participant.js");
module.exports = Participant;
module.exports.Participant = class { ... }
All to no avail, all give me the same error. I've even renamed index.js to something else, thinking it was clashing with the require mechanism. Still no change.
Any help on fixing this?
UPDATE It seems to be working from within my main.js file.
Well, turns out index.js once included through a script tag resolves its current location to the location of the including HTML code. As such, to require participant.js, given your folder structure, you will have to use require('../js/participant.js'). This only affects scripts included in HTML pages using the <script> tag. All other require's will work as expected.

Categories

Resources