Skip to content

Conversation

infomiho
Copy link
Contributor

@infomiho infomiho commented Jul 22, 2025

High-level overview

This PR:

  1. Introduces the waspc/libs directory
    • This directory contains the source code of libraries
  2. Introduces the data/libs directory
    • This directory contains the built libraries
    • The built libraries will be copied to .wasp/out alongside the SDK directory so the SDK can import the libs
  3. Introduces auth library in the libs directory (named @wasp.sh/libs-auth)
    • This library contains some auth code that was extracted from the Wasp SDK
    • It demonstrates how we can build the source code with tsdown
    • It demonstrates how to pack the library using npm pack and copy it into the data dir
    • It demonstrates how to define deps e.g. oslo
    • It demonstrates how to have multiple entry points e.g. ./sdk and ./sdk/browser to be able to separate server and browser exports
  4. Introduces the concept of a WaspLib which is how we refer to the libraries in Haskell
    • We define the libs by name and this is enough to know where to find the packaged libs (.tgz files) and where to copy them
    • We also generate the dependencies needed for SDK from the WaspLib (e.g. file:../../lib-1.0.0.tgz)
    • We defined the version to be fixed to 0.0.0 since it will be replaced with the lib checksum when copied
  5. Uses the auth library in the SDK and the server
    • It uses some jwt, cookies and password helpers, these helpers depend on external deps e.g. oslo and @node-rs/argon2 (native deps)
    • It defined some React helpers in the ./sdk/browser entry to show we can split the code

Note

Note for reviewers: there are three sections:

  1. the auth library
  2. the way the auth library is used in the SDK
  3. and the machinery to package the libs and copy them to the data dir.

I'd suggest reviewing the auth library last since it's just a JS project, nothing new to see there.

How to test it locally

  1. Run ./tools/install_packages_to_data_dir.sh
  2. Start the waspc/examples/todoApp with the cabal run or your alias of choice e.g. wrun
  3. Try using Auth UI to verify the browser code works
  4. Try to login with email to verify password hashing works, reset a password to verify JWTs still work, try OAuth to verify cookies still work

I've tested it in development and production build modes and it works as expected ✅

Known issues in development

You have to delete the node_modules and the @wasp.sh/libs-auth entry from package-lock.json to install the new development lib version that you built locally.

EDIT: fixed by using a checksum of the library's .tgz checksum as the version which prevents npm from using a stale version. Also, I've excluded the libs from being optimized (and then becoming stale) by Vite in development mode.

Left to do

  • We need to update the snapshot tests runner not to care about the .tgz files. I'll do this in a separate PR to keep it easier to review this one. Ignore tgz files in e2e tests #3078
  • Update development workflow in the waspc/README.md and in run script

Closes #2936
Closes #3069

@infomiho infomiho changed the title Tempaltes -> libs Moving logic from templates to libs Jul 22, 2025
@infomiho infomiho force-pushed the templates-to-libs branch 2 times, most recently from 0770d1f to 679a882 Compare July 22, 2025 09:50
Signed-off-by: Mihovil Ilakovac <[email protected]>
@infomiho infomiho force-pushed the templates-to-libs branch from 679a882 to b4e1769 Compare July 22, 2025 09:50
Copy link

cloudflare-workers-and-pages bot commented Jul 22, 2025

Deploying wasp-docs-on-main with  Cloudflare Pages  Cloudflare Pages

Latest commit: 4e6ddbb
Status: ✅  Deploy successful!
Preview URL: https://00fbde5c.wasp-docs-on-main.pages.dev
Branch Preview URL: https://templates-to-libs.wasp-docs-on-main.pages.dev

View logs

@infomiho infomiho force-pushed the templates-to-libs branch 2 times, most recently from 08f9765 to 72e2676 Compare July 22, 2025 21:35
@infomiho infomiho force-pushed the templates-to-libs branch from 72e2676 to 49d979c Compare July 22, 2025 22:15
@infomiho infomiho marked this pull request as ready for review July 31, 2025 11:33
@@ -1,13 +1,13 @@
import { parseCookies } from "@wasp.sh/libs-auth";
Copy link
Contributor Author

@infomiho infomiho Jul 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made this refactor by mistake not realizing that I'm touching the server and not the SDK. It works because the @wasp.sh/libs-auth is installed in the root node_modules... but it breaks our philosophy that we introduced with 0.12.0: user imports from the SDK -> framework imports user code.

If the server imports the auth lib directly, I consider that importing the SDK since the auth lib is just an implementation detail of the SDK.

So, how do we tackle this? The server doesn't have oslo listed as one of its deps (meaning this worked just because SDK had oslo as a dep).

Do we add oslo as the dep of the server to be precise?

Can we avoid the server using oslo directly? That would mean that the server uses the SDK?

Do we consider server using the auth lib directly a violation of our 0.12.0 philosophy?

cc: @sodic

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a straightforward solution would be to have libs-auth-server and libs-auth-sdk etc
Or the same library with multiple exports should be fine, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've encoded this worry in this issue as we'll need to figure out the naming: #3069

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Latest status: I've implemented the proposal from the linked issue in this PR #3069

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea of not "dragging" the server's import chain through the SDK if it isn't necessary. Since SDK will become heavily templated, we want to decouple it from the rest of our codebase as best we can, which means minimizing SDK exports into the framework code.

the SDK since the auth lib is just an implementation detail of the SDK.

This makes sense too because framework code importing stuff from multiple locations seems messy, and I'm 90% sure it will have to import templated stuff at some point.

I'll have to think about this some more, we can discuss it on a call.

@infomiho infomiho requested a review from Martinsos September 11, 2025 14:01
@infomiho
Copy link
Contributor Author

@Martinsos @sodic ready!

Copy link
Member

@Martinsos Martinsos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did another round!

I thoroughly reviewed all but TS code, that I didn't go into at all, because I feel others will cover that better (@FranjoMindek already did, and @sodic also should).

Comment on lines 21 to 24
Run `tools/install_libs_to_data_dir.sh` to compile the libs and copy
them into `data/`. Then you can use `./run wasp-cli` as normal. You can run
`./run install` which will run the script before installing the
Wasp CLI.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I woudl shortly explain that while developing libs, we work on them here. But when we run (dev version) of wasp binary and want it to use the latest version of libs, it will be searching for them in the data dir, not here. So we need to compile and ...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw I wondered here is there a way to avoid this copying. Can't we somehow work on developing them direclty in the data dir, but then we smartly replace them with their targz versions when installing it or something? Hm. Any idea how we could skip this copying?
Btw also possibly related issue, would love to get your opinion on it while at this: #2629 .

Copy link
Contributor Author

@infomiho infomiho Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then we smartly replace them with their targz versions when installing it or something?

We have to run something, we can't use the package as-is - either npm pack and then delete stuff or npm pack and then copy stuff. I'm not sure which solution is less complex, but I think copying is a bit cleaner.

I'll chime in on the issues you linked 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So - I'd say, let's keep the old way of doing things in this PR and let's refactor to non-copying for both libs and packages in a separate PR.

Copy link
Member

@Martinsos Martinsos Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good! Let's mention libs in that issue also then.

raw

Comment on lines 26 to 27
waspDataDirTarball :: NpmTarball LibsSourceDir,
waspDataDirTarballAbsPath :: Path' Abs File',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am confused about the difference between these two, what is the difference?
Aha ok, I do remember tarball is nothing but a name right?
So you need abs path also. But that abs path also contains that name? So redundancy? Yeah this is a bit confusing for me.
And also why do you have generatedCodeDirTarball also, if it is only name in there, aren't those the same regarding data they contain?
Maybe the very concept of NpmTarball is a bit confusing, because you know, what is that, how can you have Tarball here in a variable hah?
My feeling is that stuff should be more simpler / obvious, but I am not yet sure how, so I am asking these questions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you need abs path also. But that abs path also contains that name? So redundancy? Yeah this is a bit confusing for me.

You are right, we didn't need to have the tarball filename and the abs path for the data dir file.

And also why do you have generatedCodeDirTarball also, if it is only name in there, aren't those the same regarding data they contain?

Nope, the generated code tarball has the checksum in the name - so it's serving a different purpose. It's the destination tarball name.

I've tried to simplify it a bit, let me know what you think!

Comment on lines 31 to 52
makeWaspLib :: String -> IO WaspLib
makeWaspLib waspLibPackageName = do
libsSourceDirPath <- getAbsLibsSourceDirPath

-- Libs have a fixed version "0.0.0" which means we use the same version for the tarballs
-- in the data directory (e.g. lib-0.0.0.tgz). When the tarballs are copied to the generated project directory,
-- the tarball filename version is replaced with the checksum of the tarball (e.g. lib-<checksum>.tgz).
let waspDataDirTarball' = Npm.Tarball.makeNpmTarball sanitizedTarballName "0.0.0"
let waspDataDirTarballAbsPath' = libsSourceDirPath </> Npm.Tarball.filename waspDataDirTarball'

waspDataDirTarballChecksum <- computeTarballChecksum waspDataDirTarballAbsPath'
let generatedCodeDirTarball' = Npm.Tarball.makeNpmTarball sanitizedTarballName waspDataDirTarballChecksum

return $
WaspLib
{ packageName = waspLibPackageName,
waspDataDirTarball = waspDataDirTarball',
waspDataDirTarballAbsPath = waspDataDirTarballAbsPath',
generatedCodeDirTarball = generatedCodeDirTarball'
}
where
sanitizedTarballName = Npm.Tarball.sanitizePackageNameForTarballName waspLibPackageName
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is for some reason very hard for me to read and understand.

I wonder if problem is in the concept of Npm.Tarball. Maybe ti should be named Npm.TarballName.

Or maybe problem is in definition of WaspLib. Maybe it should have different contents.

Or makeWaspLib should be written in a simpler manner, more clean codish.

But something is throwing me off here, all together it is very hard to wrap a head around. Well, harder than it should be, I think.

So what is really happening here? When we say makeWaspLib? We, given a name of the package in /libs, want to create an object that has information on that wasp lib, right? But we are also doing something on the disk, any effects? No, just calculating the checksum, right?

How would you describe, in a sentence or two or three, what does makeWaspLib do? But simple enough that I can understand it? While also kind of explaining what WaspLib and Npm.Tarball are, I guess?

Copy link
Contributor Author

@infomiho infomiho Sep 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried to simplify this code, let me know what you think.

This is what's going on:

  1. We have an npm library we want to use in the generated code.
  2. This library has some sort of name e.g. @wasp.sh/miho and a dummy version of 0.0.0
  3. This library is packaged with npm pack which gives us a tarball on disk wasp.sh-miho-0.0.0.tgz
  4. In order to avoid caching issues with npm install we replace the dummy version 0.0.0 with the archive checksum e.g. wasp.sh-miho-xxxxxx.tgz. Anytime the lib changes, the checksum changes and we bust npms cache.

We want to encode all of this in the WaspLib type in Haskell:

  1. Package name
  2. Abs path to the tarball in the data dir
  3. Tarball file name which will be used in the generated code

So it comes down to this:
WaspLib (packageName, waspDataDirTarballDirAbsPath, generatedCodeDirTarballFilename)


let allEntities = [userEntity, someOtherEntity]
let (_generatorWarnings, generatorResult) = runGenerator $ injectAuth allEntities userEntity
let config = makeGeneratorConfig []
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just an idea, can help with readability / intention.

Suggested change
let config = makeGeneratorConfig []
let config = let waspLibs = [] in makeGeneratorConfig waspLibs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about creating a specialized function like:

runGeneratorWithoutWaspLibs :: Generator a -> ([GeneratorWarning], Either (NonEmpty GeneratorError) a)
runGeneratorWithoutWaspLibs = runGenerator (makeGeneratorConfig [])

Copy link
Contributor Author

@infomiho infomiho Sep 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change for now:

I've updated it to be:

  -- We make a generator config with no WaspLibs because they are not needed
  -- for Dockerfile generation.
  let config = GeneratorConfig {gcWaspLibs = []}

because I didn't like the nested let ... in

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's all right! Yeah this is the safest really, why not.

@infomiho infomiho requested a review from Martinsos September 27, 2025 13:49
@infomiho
Copy link
Contributor Author

@Martinsos ready for another look!

Comment on lines +21 to +22
The filename of a npm tarball copied to the generated Wasp app contains the checksum of the
tarball, to avoid npm caching the tarball.
Copy link
Contributor

@sodic sodic Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EDIT: Yep, adding the auth lib to the optimizeDeps.exclude array sorted it out 🎉

@infomiho @cprecioso Is this a hack though? By this I mean, assuming there's a standrad:

  • Are we just lucky that NPM does not respect the standard, and Vite is actually doing things correctly?
  • Or is Vite the one not respecting the standard and optimizes the dependencies too eagerly?

Btw, I advise doing discussions such as this one in a regular comment thread that allows replies (not a final review note) because quote discussions are difficult to follow, especially on PRs.

We can continue discussing in this comment's thread.

Copy link
Member

@cprecioso cprecioso Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no standard here, just implementations loosely mimicking each other. We're just tuning ourselves to the behaviours of disparate tools trying not to step on one another.

Changing the name of the tarball file only affects said tarball file, not its installed form, so that's why Vite is not discovering the changes. It specifically checks the contents of package-lock.json.

I think doing something like a random version number every time would probably kick Vite into reoptimizing the dependency (this was floated at some point when Miho and I talked in the beginning), but in this case I prefer this workaround of opting out of optimization than the random version number which feels way more hack-ish.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, if we hook this up into npm workspaces #3159, Vite says it has some special cases for it, that should be the most semantically accurate way of setting our dependencies.

Copy link
Contributor

@sodic sodic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Went through all the comments and discussion to get context.

I'll now go deep into the review.

Comment on lines +32 to +34
-- Libs have a fixed version "0.0.0" which means we use the same version for the tarballs
-- in the data directory (e.g. lib-0.0.0.tgz). When the tarballs are copied to the generated project directory,
-- the tarball filename version is replaced with the checksum of the tarball (e.g. lib-<checksum>.tgz).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment says a lot about what we do and not a lot about why we do it.

I think the whole tarball/cache situation could use more explanation, even examples (@infomiho similar to how you explained it in the PR comments).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pleas apply them to the old script too :)

Generator/templates/**/*.jsx
Generator/templates/**/*.tsx
Generator/templates/**/*.png
Generator/libs/*.tgz
Copy link
Contributor

@sodic sodic Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This opens up a question, why didn't we ship our the SDK like this? Oh well, we maybe didn't know we could or it wasn't practical at the time.

Hm, I did know about tarballs but I didn't seriously consider them as an option.

What we did with the SDK (i.e., specifying a local path/installing a local package) was also supposed to be a well-supported normal way of installing stuff with NPM but turned out to be NPM's neglected child.

I liked the local NPM install (symlink) option because it doesn't copy or transform the data - there's always a single source of truth.
With the tarball, I think we're effectively "copying" the data twice:

  • The first step is "copy" + compression into the tarball.
  • The second step is extracting the tarball into node_modules.

At this point there's three copies of the same code: the original code, the code in the tarball, and the extracted code. It seemed like a lot of steps that could go wrong or out of sync - the entire pipeline felt too long and complicated for what I was trying to do.

With a local install, there's always only a single copy, it felt cleaner and easier to work with.

): Promise<string> => {
return createJWT(JWT_ALGORITHM, JWT_SECRET, data, options);
},
validateJWT: async <Payload>(token: string): Promise<Payload> => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's nothing special about type arguments compared to regular ones - both types and values come from either the global scope or the function scope. If anything, values are even worse because they often come from locals inside the function.

So, unless we start calling value arguments aFoo and aBar, no prefixes for type arguments :)

BTW Payload is a much better name than T in most cases. We wouldn't call a variable holding a payload v.
Just T is fine for types whenever x would be fine for a variable - a scope where we have nothing to communicate. In this case, I could go both ways.

For some reason, many programmers don't apply clean coding rules (e.g. proper naming) when it comes to types. That's probably a relict of when type systems were much less powerful and had special semantics compared to the rest of the code. But for a language like TypeScript, where half of the codebase is types, this only causes harm.

Copy link
Contributor

@sodic sodic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent work coming up with this system!

I went through everything except Haskell code. I do have some questions that I've layed out, ping me if you want to discuss them.

Once we sort this out, I'll go through everything again.

Comment on lines +11 to +14
"./sdk/browser": {
"types": "./dist/sdk-browser.d.ts",
"import": "./dist/sdk-browser.js"
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't use this terminology across our codebase.

Unfortunately, our terminology for this particular thing is all over the place: web app, react app, client... And I actually like browser best, so I'm not sure what to do here. The most consisten choice is probably naming it "web-app" (since that's the part of the codebase that uses it).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We decide to use tsdown as the bundler for our libraries and I liked their terminology quite a lot:

  • node
  • browser
  • neutral

It's more based on the platform rather than the place of usage. So I thought, okay, let's stick with no suffix for neutral (most of our code) and let's add the /browser suffix for browser specific code (stuff that won't be able to run on the server). I guess later on, we'd find that we might need the /node suffix as well for server only code.

So the system we have in the end is:

@wasp.sh/<lib-name>/<place>/<platform> where <place> is sdk | server | web-app and <platform> is <empty> | browser | node.

"import": "./dist/sdk.js"
},
"./sdk/browser": {
"types": "./dist/sdk-browser.d.ts",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the types part necessary? I think it might be automatic.

Copy link
Contributor Author

@infomiho infomiho Sep 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without types, npx @arethetypeswrong/cli -P reports that resolution will fail when node16 resolution is used. I don't know if we care about that since it's our internal library, but I find it nice that our lib is as compliant as possible 😄

+ the default tsdown template includes the types: https://github.com/Gugustinette/create-tsdown/blob/main/templates/default/package.json

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be automatic but the interaction with exports is a bit iffy. It's better to set it.

"check:type-exports": "npx @arethetypeswrong/cli -P",
"check:types": "tsc --noEmit",
"dev": "vitest dev",
"prepare": "npm run build"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a prepare script that just calls the build script. How come?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prepare script is called by npm when we call npm pack - this way the package is always built before packaging.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prepare is the name that npm expects, it can do anything. Here we're explicitly saying what it does to "prepare" the package is to build it.

"verbatimModuleSyntax": true,
"skipLibCheck": true
},
"include": ["src", "tests/sdk/password.test.ts", "tests/sdk/jwt.test.ts"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just "tests"?

@@ -0,0 +1,20 @@
{
"compilerOptions": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How did we decide on the options here? For example, how come lib is 2023? Are missing noEmit?

Could we group options as we do in sdk/tsconfig.json? Since we'll have many libraries, I'd like our tsconfg files to be as standardized as possible. It would also be great if we had a "source of truth" for this, any ideas?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went with the default template for the tsdown bundler, keeping with the theme "these are just JS libs" and didn't want to do anything special for Wasp in that department: https://github.com/Gugustinette/create-tsdown/blob/main/templates/default/tsconfig.json

In my mind, if tsdown updates, we just follow their upgrade guides and don't need to worry about anything special that we introduced. Does that make sense?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Franjo had the idea to extract the prettier configuration to its own package/repo. In my monorepo projects, I do that, and not only with prettier but tsconfigs, build utilities etc. In that way you don't need to have a bunch of options everywhere.

@@ -1,13 +1,13 @@
import { parseCookies } from "@wasp.sh/lib-auth/server";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if this matters (probably not), but I'd expect our lib imports to come below "normal" imports like express.


import type { ProviderConfig } from 'wasp/auth/providers/types';
import { config } from 'wasp/server';
import type { ProviderConfig } from "wasp/auth/providers/types";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double quotes? Is this a formatting mistake?

@@ -1 +1,2 @@
packages
Generator/libs/*.tgz
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mentioned this in a different thread, but why don't we ignore the entire libs directory and have the exact same setup as we do with packages?

data_libs_dir=$waspc_dir/data/Generator/libs

# Cleanup old libs.
rm -f "${data_libs_dir:?}"/*.tgz
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The :? check doesn't do anything. There's no way data_libs_dir is unset. It could be wrong (e.g., if script_dir is unset), but it's always set so :? won't trigger.


for package in $(ls "$dir/../packages"); do
package_dir="$dir/../packages/$package"
for package_dir in "${waspc_dir:?}"/packages/*; do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The :? doesn't do anything here. There's no way that waspc_dir is unset. It's always at least /...
We should do this check with script_dir instead.

Btw, I think this idiom also alolws setting an error message, that might come in handy :)

@cprecioso cprecioso self-requested a review September 30, 2025 10:47
Copy link
Member

@cprecioso cprecioso left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mid-level review (read through the files, but didn't read through all the old discussions, feel free to resolve stuff if it was already discussed), added some comments. I haven't tried running yet, I'm testing that and will send an approval once I've seen it in action.


depsMessage :: AppSpec -> String
depsMessage appSpec =
depsMessage :: AppSpec -> [WaspLib.WaspLib] -> String
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should be showing the libraries, they should be transparent and have no significative version number by themselves. And if I got it right, users should not be importing the libraries.

export const ForgotPasswordForm = () => {
const { register, handleSubmit, reset, formState: { errors } } = useForm<{ email: string }>()
const { isLoading, setErrorMessage, setSuccessMessage, setIsLoading } = useContext(AuthContext)
const { isLoading, setErrorMessage, setSuccessMessage, setIsLoading } = useAuthContext()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually think it's good that he made this change, it's scoped enough to be a demonstration and a proof that it works, without big changes otherwise.

@@ -1,14 +1,13 @@
{{={= =}=}}
import { useState, createContext, useMemo } from 'react'
import { useState, useMemo, type CSSProperties } from 'react'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import { useState, useMemo, type CSSProperties } from 'react'
import { useState, useMemo } from 'react'

function normalizePassword(password: string): string {
return password.normalize("NFKC");
}
export { hashPassword, verifyPassword } from "@wasp.sh/lib-auth/sdk";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree!


getAllNpmDeps :: AppSpec -> Either String AllNpmDeps
getAllNpmDeps spec =
getAllNpmDeps :: AppSpec -> [WaspLib.WaspLib] -> Either String AllNpmDeps
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels weird for me that we're passing this around as a parameter. In my mind it's either global (the static list of available libs), or a property of the AppSpec (which libraries we need for the current app).

"module": "preserve",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"types": ["node"],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? If they're set installed TypeScript just picks them up

"import": "./dist/sdk.js"
},
"./sdk/browser": {
"types": "./dist/sdk-browser.d.ts",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be automatic but the interaction with exports is a bit iffy. It's better to set it.

"exports": {
"./sdk": {
"types": "./dist/sdk.d.ts",
"import": "./dist/sdk.js"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're already saying "type": "module", we don't need to specify import here too. I'd use default in case some random tool forgets to set the import conditions.

"build": "tsdown",
"check": "npm run check:types && npm run check:coverage && npm run check:type-exports",
"check:coverage": "vitest run --coverage",
"check:type-exports": "npx @arethetypeswrong/cli -P",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do remote code execution 😁? Just install it as a dev dep.

"check:type-exports": "npx @arethetypeswrong/cli -P",
"check:types": "tsc --noEmit",
"dev": "vitest dev",
"prepare": "npm run build"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prepare is the name that npm expects, it can do anything. Here we're explicitly saying what it does to "prepare" the package is to build it.


# Building
WASP_PACKAGES_COMPILE="${SCRIPT_DIR}/tools/install_packages_to_data_dir.sh"
WASP_LIBS_COMPILE="${SCRIPT_DIR}/tools/install_libs_to_data_dir.sh"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also add a ./run build:libs command like the packages one?

],
optimizeDeps: {
exclude: ['wasp']
exclude: {=& depsExcludedFromOptimization =}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
exclude: {=& depsExcludedFromOptimization =}
exclude: {=& depsExcludedFromOptimization =}

"tsconfig.tsbuildinfo",
"dist"
]
&& not (isTgzFile filePath)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has there been discussion about ignoring the tgz? Can you point me to it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Figuring out how we use libs in the framework code Create an initial POC for moving templated code to libraries
5 participants