App reviews, troubleshooting, and recommendations
Hello
I used
npm init @shopify/app@latest
to create a Shopify node app, then I generated Theme App Extension usin
npm run generate extension
In my extension I did create a block called home-page.liquid under /blocks and I created main.js and main.css under /assets folder
Here is my architecture
Problem:
There are so many lines of code in main.js and main.css. so I tried splitting the code into multiple files using:
And then I import it into main.js file
Is there a solution to code splitting?
Also to implement Typescript
Thank you in advance
Bit of a late response here, but I figure this could be useful for anyone else checking this post out.
I struggled with this same issue and recently decided to use rollup js to bundle up my js files (you could also implement ts though in my case I didn’t).
After figuring it out I made a blog post so that others would be able to do something similar 🙂 you could probably do something similar with vite and then have it build to your extensions folder every time you save, but I didn’t end up doing that since it was just complicating what already worked.
Blog Post: https://www.mitchellmudd.dev/blog/implementing-code-splitting-shopify-theme-extensions-with-rollup
Hope this helps someone 😊
Thank you Mitchelldirt, this is exactly what I needed! My only critique for your tutorial is that you have to add "type": "module" to the package.json file in order for it to compile with `npm run build`.
I wish it was possible to "natively" use multiple javascript files in a theme app extension, but I'm glad I was able to use your workaround (it was the only workaround I could find!).
Ahhhhh yes - forgot to add that when I was writing the blog post. I updated the post and the linked source code 🙂
Thanks for letting me know and I'm glad that I was able to help you out!
Hey @mitchelldirt, I had a follow-up question. How do I update the rollup.config.js file to work with multiple app blocks? Like I want to give each app block its own corresponding javascript file, instead of bundling everything into a single javascript file called "app.js" as it's doing currently.
I've figured out a solution that works with multiple app blocks. I edited the rollup.config.js and <app-block-name>.liquid files slightly and am using the SystemJS loader.
First, create your new app block and corresponding JavaScript file. For the sake of this tutorial, let's call the new app block "block2.liquid" and corresponding JavaScript file "app2.js".
Then, edit your rollup.config.js to look like:
import commonjs from "@rollup/plugin-commonjs";
import copy from "rollup-plugin-copy";
const input = {
app: "./assets/app.js",
app2: "./assets/app2.js",
};
export default {
input,
output: {
dir: "../../extensions/code-splitting-example/assets",
format: "system",
},
plugins: [
commonjs(),
copy({
targets: [
{ src: "./assets/*.css", dest: "../../extensions/code-splitting-example/assets" },
{ src: "./assets/system.js", dest: "../../extensions/code-splitting-example/assets" },
{
src: "./blocks/*.liquid",
dest: "../../extensions/code-splitting-example/blocks",
},
],
}),
],
};
You can see I made 3 changes: I added the path to the new JavaScript file associated with my second app block ("./assets/app2.js), I set the output format to "system" so that it uses the SystemJS format, and I added a copy target which copies a new "system.js" file to my "extensions" directory.
To download this new system.js file, go to the SystemJS Readme - Overview, and download the loader of your choosing. I chose the regular system.js loader (option 2). Then, rename the downloaded file to "system.js", and place it in the folder "dev-extensions/code-splitting-example/assets".
In each of your <app-block-name>.liquid files, paste the following code at the top (replace "app2.js" in the code below with whatever you called the JavaScript file associated with your given app block):
<script src="{{ 'system.js' | asset_url }}" defer></script>
<script type="systemjs-module" src="{{ 'app2.js' | asset_url }}" defer></script>
After making the above changes, running `npm run build` in the "dev-extensions/code-splitting-example" directory should generate files in a format that still works with Shopify Theme App Extensions, even when you have multiple app blocks.
Hi,
I'm trying to set something similar up with my shopify theme but I've run into a snag when it comes to dynamic imports.
It's hard to explain, but basically I want a big chunk of code to load but only when the user clicks a button.
The only neat way of doing this with rollup and everything else is to use dynamic es imports in my source code, which gets converted into SystemJS.import()s.
However, Shopify likes to append a long string of numbers to version every time you upload an asset (ie, code).
SystemJS doesn't know this and tries to load the original version of the uploaded script, which has long since changed. Not only that but it also has a dependency check that causes it to also load the original uploaded version of the calling script!
Those ?v= arguments are important.
Shopify never make it easy....
I could change it so rollup outputs the files using the default template `[name]-[hash].js`, but this doesn't address the issue of SystemJS attempting to load dependencies (app.system.js) because it doesn't recognize that 'app.system.js?v=38838934239889749121708712258' is the actual dependency and is already loaded.
Any ideas?
EDIT
The problem is a bit worse than I thought. forget dynamic imports.
I have a component called 'flipdown' which is embedded in its own section type.
In sections/flipdown.liquid:
<script type="systemjs-module" src="{{ "flipdown.system.js" | asset_url }}" defer></script>
{{ "flipdown.css" | asset_url | stylesheet_tag }}
<div data-component="timer" data-date-end="{{ date }}"></div>
Whats up with this difficult stuff? Why should I install a bundler, ffs?
You have two files - lets call them main.js and helper.js
In helper.js you can define some helper functions and then exporting them:
export const numberFormat(number) => {
return `Number: ${number}`;
}
In main.js you call you first import the function:
import { numberFormat } from './helper.js';
Lastly, when you use the script tag, you must use it like so: (note the type="module")
<script type="module" src="{{ 'main.js' | asset_url }}" defer></script>
The css is similar, but easier.
You have the styles in main.css and other.css. Then in main.css:
@import url('./other.css');
The referenced stylesheet in the schema should be of course main.css.
Please dont go confuse people with complicated solutions! This is basic java script and css in its finest! Of course, the bundler has advantages such as minification and obfuscation.
However, if you are going to do this, the chances are you have a big extension and then you can just use a framework, which at build outputs all its files in the asset folder.
@AlexAngelov, you're right that it shouldn't be this complicated, but unfortunately it is. At least from my experience, your example code above does not work with Shopify theme app extensions. Myself and others have had to come up with these stupid workarounds in order to be able to separate our code into multiple files and reuse code.
But if you have any simpler solution that you've tested and verified that it works with theme app extensions, please do share it here!
I posted this because I literally implemented it a few hours ago. It works fine, and there is no reason it shouldn’t - because that’s how JavaScript works as of ECMA6.
In my solution I have two files - one with the main function and one with shared functions - which I am using in the react admin and in the theme extension (hence why I needed the code split).
@AlexAngelov Oh that is awesome to hear! Maybe they fixed the issue sometime after I experienced it about a month and a half ago? Cause I swear the code you posted is exactly what I first tried (cause you're right; that way of doing it should normally work). Maybe I'll try again to see if it's working for me now. Thanks for your reply and bringing this to my attention.
Confirming that this does work and should probably be the accepted answer!
There's one caveat that you might need to consider the timing of the script load, because this method behaves differently than the `schema.javascript` import, from what I experienced. I'm not sure how or why, but some initialization of elements was broken after switching.
As 2024 wraps up, the dropshipping landscape is already shifting towards 2025's trends....
By JasonH Nov 27, 2024Hey Community! It’s time to share some appreciation and celebrate what we have accomplis...
By JasonH Nov 14, 2024In today’s interview, we sat down with @BSS-Commerce to discuss practical strategies f...
By JasonH Nov 13, 2024