Skip to content

Commit f37cb70

Browse files
authored
Create babel plugin to handle lazy loading (#4)
* feat: create babel plugin to handle lazy loading * fix: update typescript CI to also run the tests in the pipelines * doc: update lazy loading component inline documentation * fix: typescript pipeline * fix: update CI typescript test command * fix: prevent duplicate pipelines on PR * feat: create separate vite plugin package * chore: refined lazy fn package * test: add test to lazy fn plugin * feat: update version to v0.1.5
1 parent 6498905 commit f37cb70

21 files changed

Lines changed: 323 additions & 64 deletions

File tree

.github/workflows/rust.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ env:
1515

1616
jobs:
1717
build_and_test:
18+
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
1819
name: Build and test rust crates
1920
runs-on: ubuntu-latest
2021
strategy:

.github/workflows/typescript.yml

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,20 @@ on:
1212

1313
jobs:
1414
build-and-test:
15+
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
16+
timeout-minutes: 15
1517
runs-on: ubuntu-latest
1618

17-
strategy:
18-
matrix:
19-
node-version: [18.x, 20.x]
20-
2119
steps:
22-
- uses: actions/checkout@v2
23-
- name: Use Node.js ${{ matrix.node-version }}
24-
uses: actions/setup-node@v2
20+
- name: Check out code
21+
uses: actions/checkout@v4
22+
with:
23+
fetch-depth: 2
24+
25+
- name: Setup Node.js environment
26+
uses: actions/setup-node@v4
2527
with:
26-
node-version: ${{ matrix.node-version }}
28+
node-version: 20
2729

2830
- name: Install pnpm
2931
run: npm i -g pnpm
@@ -34,15 +36,19 @@ jobs:
3436
- name: Build project
3537
run: pnpm build
3638

39+
- name: Test project
40+
run: pnpm test
41+
3742
fmt-lint-and-types:
38-
runs-on: ubuntu-20.04
43+
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
44+
runs-on: ubuntu-latest
45+
timeout-minutes: 15
3946

4047
steps:
41-
- uses: actions/checkout@v2
42-
- name: CI Checkout
43-
uses: actions/setup-node@v2
48+
- name: Check out code
49+
uses: actions/checkout@v4
4450
with:
45-
node-version: 20.x
51+
fetch-depth: 2
4652

4753
- name: Install pnpm
4854
run: npm i -g pnpm

crates/tuono/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "tuono"
3-
version = "0.1.4"
3+
version = "0.1.5"
44
edition = "2021"
55
authors = ["V. Ageno <valerioageno@yahoo.it>"]
66
description = "The react/rust fullstack framework"

crates/tuono_lib/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "tuono_lib"
3-
version = "0.1.4"
3+
version = "0.1.5"
44
edition = "2021"
55
authors = ["V. Ageno <valerioageno@yahoo.it>"]
66
description = "The react/rust fullstack framework"
@@ -24,5 +24,5 @@ serde = { version = "1.0.202", features = ["derive"] }
2424
erased-serde = "0.4.5"
2525
serde_json = "1.0"
2626

27-
tuono_lib_macros = {path = "../tuono_lib_macros", version = "0.1.4"}
27+
tuono_lib_macros = {path = "../tuono_lib_macros", version = "0.1.5"}
2828

crates/tuono_lib_macros/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "tuono_lib_macros"
3-
version = "0.1.4"
3+
version = "0.1.5"
44
edition = "2021"
55
description = "The react/rust fullstack framework"
66
homepage = "https://github.com/Valerioageno/tuono"

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "tuono",
3-
"version": "0.1.4",
3+
"version": "0.1.5",
44
"description": "",
55
"main": "src/index.js",
66
"packageManager": "pnpm@9.1.1",
@@ -9,8 +9,8 @@
99
"build": "turbo build",
1010
"lint": "turbo lint",
1111
"format": "turbo format",
12-
"format:check": "turbo format:check",
13-
"types": "turbo types",
12+
"format:check": "turbo format:check",
13+
"types": "turbo types",
1414
"test": "turbo test"
1515
},
1616
"keywords": [],
@@ -36,6 +36,6 @@
3636
"vitest": "^1.5.2"
3737
},
3838
"dependencies": {
39-
"turbo": "^2.0.3"
39+
"turbo": "^2.0.4"
4040
}
4141
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
dist
2+
pnpm-lock.yaml
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# tuono-lazy-fn-vite-plugin
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"name": "tuono-lazy-fn-vite-plugin",
3+
"version": "0.1.5",
4+
"description": "Plugin for the tuono's lazy fn. Tuono is the react/rust fullstack framework",
5+
"scripts": {
6+
"dev": "vite build --watch",
7+
"build": "vite build",
8+
"lint": "eslint --ext .ts,.tsx ./src -c ../../.eslintrc",
9+
"format": "prettier -u --write --ignore-unknown '**/*'",
10+
"format:check": "prettier --check --ignore-unknown '**/*'",
11+
"types": "tsc --noEmit",
12+
"test": "vitest run"
13+
},
14+
"keywords": [],
15+
"author": "Valerio Ageno",
16+
"license": "MIT",
17+
"type": "module",
18+
"types": "dist/esm/index.d.ts",
19+
"main": "dist/cjs/index.cjs",
20+
"module": "dist/esm/index.js",
21+
"files": [
22+
"dist",
23+
"src",
24+
"README.md"
25+
],
26+
"exports": {
27+
".": {
28+
"import": {
29+
"types": "./dist/esm/index.d.ts",
30+
"default": "./dist/esm/index.js"
31+
},
32+
"require": {
33+
"types": "./dist/cjs/index.d.cts",
34+
"default": "./dist/cjs/index.cjs"
35+
}
36+
},
37+
"./package.json": "./package.json"
38+
},
39+
"dependencies": {
40+
"@babel/core": "^7.24.4",
41+
"@babel/types": "^7.24.0",
42+
"vite": "^5.2.11"
43+
},
44+
"devDependencies": {
45+
"@tanstack/config": "^0.7.11",
46+
"@types/babel__core": "^7.20.5",
47+
"prettier": "^3.2.4",
48+
"vitest": "^1.5.2"
49+
}
50+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import type { Plugin } from 'vite'
2+
import * as babel from '@babel/core'
3+
import type { PluginItem } from '@babel/core'
4+
5+
import * as t from '@babel/types'
6+
7+
import type {
8+
Identifier,
9+
ImportDeclaration,
10+
CallExpression,
11+
ArrowFunctionExpression,
12+
StringLiteral,
13+
} from '@babel/types'
14+
15+
/**
16+
* This plugin just removes the `lazy` imported function from any tuono import
17+
*/
18+
const RemoveTuonoLazyImport: PluginItem = {
19+
name: 'remove-tuono-lazy-import-plugin',
20+
visitor: {
21+
ImportSpecifier: (path) => {
22+
if ((path.node.imported as Identifier).name === 'lazy') {
23+
if (
24+
(path.parentPath.node as ImportDeclaration).source.value === 'tuono'
25+
) {
26+
path.remove()
27+
}
28+
}
29+
},
30+
},
31+
}
32+
33+
/**
34+
* Import { lazy } from 'react'
35+
*/
36+
const ImportReactLazy: PluginItem = {
37+
name: 'import-react-lazy-plugin',
38+
visitor: {
39+
Program: (path: any) => {
40+
let isReactImported = false
41+
42+
path.node.body.forEach((val: any) => {
43+
if (val.type === 'ImportDeclaration' && val.source.value === 'react') {
44+
isReactImported = true
45+
// TODO: Handle also here case of already imported react
46+
// Right now works just for the main routes file
47+
}
48+
})
49+
50+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
51+
if (!isReactImported) {
52+
const importDeclaration = t.importDeclaration(
53+
[t.importSpecifier(t.identifier('lazy'), t.identifier('lazy'))],
54+
t.stringLiteral('react'),
55+
)
56+
path.unshiftContainer('body', importDeclaration)
57+
}
58+
},
59+
},
60+
}
61+
62+
/**
63+
* For the server side we need to statically import the lazy loaded components
64+
*/
65+
const TurnLazyToStaticImport: PluginItem = {
66+
name: 'turn-lazy-to-static-import-plugin',
67+
visitor: {
68+
VariableDeclaration: (path) => {
69+
path.node.declarations.forEach((el) => {
70+
const init = el.init as CallExpression
71+
if ((init.callee as Identifier).name === 'lazy') {
72+
const importName = (el.id as Identifier).name
73+
const importPath = (
74+
(
75+
(init.arguments[0] as ArrowFunctionExpression)
76+
.body as CallExpression
77+
).arguments[0] as StringLiteral
78+
).value
79+
80+
if (importName && importPath) {
81+
const importDeclaration = t.importDeclaration(
82+
[t.importDefaultSpecifier(t.identifier(importName))],
83+
t.stringLiteral(importPath),
84+
)
85+
86+
path.replaceWith(importDeclaration)
87+
}
88+
}
89+
})
90+
},
91+
},
92+
}
93+
94+
export function LazyLoadingPlugin(): Plugin {
95+
return {
96+
name: 'vite-plugin-tuono-lazy-loading',
97+
enforce: 'pre',
98+
transform(code, _id, opts): string | undefined | null {
99+
if (code.includes('lazy') && code.includes('tuono')) {
100+
const res = babel.transformSync(code, {
101+
plugins: [
102+
['@babel/plugin-syntax-jsx', {}],
103+
['@babel/plugin-syntax-typescript', { isTSX: true }],
104+
[RemoveTuonoLazyImport],
105+
[!opts?.ssr ? ImportReactLazy : []],
106+
[opts?.ssr ? TurnLazyToStaticImport : []],
107+
],
108+
sourceMaps: true,
109+
})
110+
111+
return res?.code
112+
}
113+
return code
114+
},
115+
}
116+
}

0 commit comments

Comments
 (0)