require in Sapper preload function - javascript

I am trying to parse some markdown before a page loads, so I have the following code:
<script context="module">
var markdown = require( "markdown" ).markdown;
export async function preload(page, session) {
var someMakrdown = '# Title'
var html = markdown.toHTML(someMakrdown)
return {post : html}
}
</script>
This fails with a 500 and the message: require is not defined
I have also tried using import in the following way:
<script context="module">
import {markdown} from 'markdown';
export async function preload(page, session) {
var someMakrdown = '# Title'
var html = markdown.toHTML(someMakrdown)
return {post : html}
}
</script>
This also fails with a 500 and the message Error resolving module specifier: util
I have got this to work by moving the code into a [slug].json.js file and calling that from within the preload, but is there a better way to do this?
One of the things that I've enjoyed so far about Svelte is keeping the HTML, CSS and JS together for each component. It just doesn't feel right that I need to call a seperate JS file to create JSON that can then be used.

It appears that the markdown module imports util, making it unsuitable for client-side use. Since preload functions run both server-side and client-side, that's no good. My suggestion would be to use a different library instead (I can recommend marked) and/or raise an issue on the markdown bug tracker.

Just in case someone needs a complete answer, here's how it looks like using marked, as proposed by Rich.
<script context="module">
import marked from 'marked';
let elem = '';
export function preload({ params, query }) {
elem = marked('# Marked in the server or/and browser');
}
</script>
<div>{#html elem}</div>
It'll run in the server and then, for subsequent navigation, in the browser.

Related

Elixir Phoenix - How to create and import javascript files into specific templates

I'm currently experimenting with the Elixir Phoenix framework together with Liveview. For my project, I would like to write some Javascript code that is only imported on certain pages (templates). Although this seems like something very trivial, I am struggling to get it working.
At this moment I created a seperate Javascript file as such assets/js/custom.js. After doing this, I added the following line to my root.html.heex as a first test to see if this already works. For this line, I simply looked at how app.js is imported.
<script defer phx-track-static type="text/javascript" src={Routes.static_path(#conn, "/assets/custom.js")}></script>
The next step would then be to figure out how to import it in a seperate template instead of the root. However, this first test already failed resulting in the following error:
[debug] ** (Phoenix.Router.NoRouteError) no route found for GET /assets/custom.js (MyAppWeb.Router)
(my_app 0.1.0) lib/phoenix/router.ex:405: MyAppWeb.Router.call/2
(my_app 0.1.0) lib/my_app_web/endpoint.ex:1: MyAppWeb.Endpoint.plug_builder_call/2
(my_app 0.1.0) lib/plug/debugger.ex:136: MyAppWeb.Endpoint."call (overridable 3)"/2
(my_app 0.1.0) lib/my_app_web/endpoint.ex:1: MyAppWeb.Endpoint.call/2
(phoenix 1.6.15) lib/phoenix/endpoint/cowboy2_handler.ex:54: Phoenix.Endpoint.Cowboy2Handler.init/4
(cowboy 2.9.0) c:/Users/arnod/Desktop/phoenixtut/my_app/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
(cowboy 2.9.0) c:/Users/arnod/Desktop/phoenixtut/my_app/deps/cowboy/src/cowboy_stream_h.erl:306: :cowboy_stream_h.execute/3
(cowboy 2.9.0) c:/Users/arnod/Desktop/phoenixtut/my_app/deps/cowboy/src/cowboy_stream_h.erl:295: :cowboy_stream_h.request_process/3
(stdlib 4.0.1) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
Could somebody help me figure this one out? How does one add seperate Javascript files and only import them in specific templates?
You can import all your custom javascript once in app.js, assign them as hooks which you can then use in your (live) views, wherever needed, for example;
custom.js
export const SomeFunction = {
mounted() {
alert("some function ran!");
}
}
app.js snippet
...
import {SomeFunction} from "./custom.js"
let Hooks = {}
Hooks.SomeFunction = SomeFunction
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}, hooks: Hooks})
...
Then in your live view render function (or template) add the hook
...
def render(assigns) do
~H"""
...
<div id="my-hook" phx-hook="SomeFunction"></div>
...
end
...
More about javascript interoperability can be found on the Phoenix hex page here. You can tie them to all sorts of phoenix events.
nb. Also note that #conn isn't available in live views, only #socket is.

How to import octokit in browser

tried to follow an example from this. To import octokit I first pasted the following lines into my html document as stated by adding the following lines:
<script type="module">
import { Octokit } from "https://cdn.skypack.dev/#octokit/core";
</script>
This just resulted in an Uncaught (in promise) ReferenceError: Octokit is not defined. Moving the line in script tags directly into the script.js file above the code just made one of the main functions appear as undefined (it handled a button click that authenticated a google account and then retrieved data from google sheets via the sheets API).
I then tried all this with new html and js files, adding that code directly to the html had the same result but this time adding it to the start of the js file gives a import declarations may only appear at top level of a module script.js:1
Don't really know what to do from here so any help would be appreciated. Did find this though but it just says the issue is fixed?
I think the best solution is to write your script logic in a separate javascript file, and then import that file in your html at the end of body element.
Here is an example of getting your repositories from GitHub:
repos.js
import { Octokit } from "https://cdn.skypack.dev/octokit";
import { config } from "./config.js";
const GIT_KEY = config.API_KEY;
export const octokit = new Octokit({
auth: GIT_KEY,
});
const repo = await octokit.request(
"GET /users/{username}/repos{?type,sort,direction,per_page,page}",
{
username: "your_username",
}
);
console.log(repo);
repos.html:
<body>
...
<script type="module" src="repos.js"></script>
</body>

JavaScript/TypeScript Dynamic Imports and browsers - options

I have a relatively simple requirement to start out with.
I want to be able to load a particular UI component, based on some dynamic context. I was thinking that dynamic imports could be used for this purpose. The dynamic imports should be loaded through the Web browser (no nodeJS).
Details about what is possible with dynamic imports are very sketchy to me - at best, probably also because I'm not an expert in the field of JavaScript/TypeScript (yet).
I'm using vanilla JS at the moment and vitejs as build tool.
Here is what I have so far.
This is my main.ts file:
const getPath = () => {
if (import.meta.env.MODE === 'development') {
return 'http://127.0.0.1:3000/one.js';
} else {
return 'http://127.0.0.1:5000/one.js';
}
};
import(getPath()).then((Module) => {
Module.default();
});
This already makes vitejs barf, complaining that it cannot analyze things - but I'm ignoring that for now. The one.js file looks like this:
const hello = () => {
console.log('Hello from one');
};
export default hello;
Both run dev as run serve work with this setup, as-in, the message is printed on the browser console.
My next thing I want to see working is how one.js would be able to itself import a module and work with that. I've tried this with moment like so:
import moment from "moment";
const hello = () => {
console.log('Hello from one ', moment().format());
};
export default hello;
This errors with:
Uncaught (in promise) TypeError: Error resolving module specifier “moment”. Relative module specifiers must start with “./”, “../” or “/”.
Now I don't know whether what I want to do, is not support or that I'm just not doing it the right way. Any pointers would be appreciated.

How to best import "server-only" code in Next.js?

In the getServerSideProps function of my index page, I'd like to use a function foo, imported from another local file, which is dependent on a certain Node library.
Said library can't be run in the browser, as it depends on "server-only" modules such as fs or request.
I've been using the following pattern, but would like to optimize it. Defining foo as mutable in order to have it be in scope is clunky and seems avoidable.
let foo;
if (typeof window === "undefined") {
foo = require("../clients/foo");
}
export default function Index({data}) {
...
}
export async function getServerSideProps() {
return {
props: {data: await foo()},
}
}
What would be the best practice here? Is it somehow possible to leverage ES6's dynamic import function? What about dynamically importing within getServerSideProps?
I'm using Next.js version 9.3.6.
Thanks.
UPDATE:
It seems as if Next.js's own dynamic import solution is the answer to this. I'm still testing it and will update this post accordingly, when done. The docs seem quite confusing to me as they mentionn disabling imports for SSR, but not vice versa.
https://nextjs.org/docs/advanced-features/dynamic-import
When using getServerSideProps/getStaticProps, Next.js will automatically delete any code inside those functions, and imports used exclusively by them from the client bundle. There's no risk of running server code on the browser.
However, there are a couple of considerations to take in order to ensure the code elimination works as intended.
Don't use imports meant for the server-side inside client-side code (like React components).
Ensure you don't have unused imports in those files. Next.js won't be able to tell if an import is only meant for the server, and will include it in both the server and client bundles.
You can use the Next.js Code Elimination tool to verify what gets bundled for the client-side. You'll notice that getServerSideProps/getStaticProps gets removed as do the imports used by it.
Outside of getServerSideProps/getStaticProps, I found 2 fairly similar solutions.
Rely on dead code elimination
In next.config.js:
config.plugins.push(
new webpack.DefinePlugin({
'process.env.RUNTIME_ENV': JSON.stringify(isServer ? 'server' : 'browser'),
}),
);
export const addBreadcrumb = (...params: AddBreadcrumbParams) => {
if (process.env.RUNTIME_ENV === 'server') {
return import('./sentryServer').then(({ addBreadcrumb }) => addBreadcrumb(...params));
}
return SentryBrowser.addBreadcrumb(...params);
};
Note that some for reason I don't understand, dead code elimination does not work well if you use async await, or if you use a variable to store the result of process.env.RUNTIME_ENV === 'server'. I created a discussion in nextjs github.
Tell webpack to ignore it
In next.config.js
if (!isServer) {
config.plugins.push(
new webpack.IgnorePlugin({
resourceRegExp: /sentryServer$/,
}),
);
}
In that case you need to make sure you will never import this file in the client otherwise you would get an error at runtime.
You can import the third party library or a serverside file inside getServerSideProps or getInitialProps since these functions run on server.
In my case I am using winston logger which runs on server only so importing the config file only on server like this
export async function getServerSideProps (){
const logger = await import('../logger');
logger.info(`Info Log ->> ${JSON.stringify(err)}`);
}
You can also import library/file which has default export like this
export async function getServerSideProps(context) {
const moment = (await import('moment')).default(); //default method is to access default export
return {
date: moment.format('dddd D MMMM YYYY'),
}
}

What is the correct way to split firebase cloud functions into different files?

I'm trying to split a large cloud function into a smaller more maintainable files. However when deploying I always get the same error:
Error occurred while parsing your function triggers.
/Users/Raphi/Documents/Programing/Development/merchantAPI/functions/index.js:2
import { DirectPost } from '../functions/merchantPost';
^^^^^^
SyntaxError: Cannot use import statement outside a module
I spent most of last night googling but there doesn't seem to be any documentation for this
simplified index.js:
import { DirectPost } from '../functions/merchantPost';
exports.portal = functions.https.onRequest(async (request, response) => {
var charge = new DirectPost()
})
simplified merchantPost.js:
export class DirectPost {
constructor(){}
}
Try Including JavaScript class definition from another file in Node.js.
Also consider making a separate file for the "portal handler" callback, so that you can do
exports.portal = functions.https.onRequest(portalHandler);
It makes your index.js much cleaner.
Also consider using TypeScript (in which importing stuff is done exactly as you're trying to in your code) which will help you in the long run by preventing some nasty bugs...

Categories

Resources