Skip to content

Commit d2ecd86

Browse files
committed
feat(starters): add htmx+go and fix go starter
• Added the HTMX + Go starter with a Go-rendered page, HTMX fragment endpoint, refresh-button with swap, agent support, CLI metadata, starter archive wiring, docs updates, and HTMX logo • Fixed the existing Go starter by correcting bad Elements bundle URLs, loading CSS and JS from the right packages, removing the frontend dependency/setup path, fixing stale copy, updating source links, simplifying template data • More Go server functionality with PORT support, root-only routing, explicit HTML content type, and template execution error handling. • Go and HTMX+Go frontend build tooling removed, now using browser CDN bundles, excluding local build/cache outputs, skipping Elements dependency setup, and stamping CDN bundle versions during archive generation. • Test updates including Elements version stamping logic Signed-off-by: Jake Guza <jguza@nvidia.com>
1 parent 1cda122 commit d2ecd86

34 files changed

Lines changed: 661 additions & 80 deletions

File tree

‎.github/actions/setup-ci/action.yml‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ runs:
1313
node-version-file: './.nvmrc'
1414
registry-url: 'https://registry.npmjs.org'
1515
cache: 'pnpm'
16+
- uses: actions/setup-go@v6
17+
with:
18+
go-version: '1.26.x'
19+
cache: false
1620
- name: Install dependencies
1721
shell: bash
1822
run: pnpm install --frozen-lockfile

‎.prettierignore‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ projects/starters/bundles/src/index.html
3030
projects/starters/eleventy/src/_layouts/index.11ty.js
3131
projects/starters/eleventy/src/index.11ty.js
3232
projects/starters/go/src/index.html
33+
projects/starters/go-htmx/src/index.html
3334
projects/starters/hugo/**/*
3435
projects/starters/importmaps/src/index.html
3536
projects/starters/mpa/src/**/*.html
@@ -39,4 +40,4 @@ projects/starters/solidjs/src/App.tsx
3940
projects/starters/typescript/src/index.html
4041
projects/starters/vue/src/**/*
4142
projects/starters/nuxt/.nuxt/**/*
42-
projects/starters/nuxt/app/**/*
43+
projects/starters/nuxt/app/**/*

‎README.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,14 @@ Examples of projects include:
3333

3434
To set up repository dependencies and run the full build, run the following commands at the **root** of the repository:
3535

36+
The CI pipeline also builds Go starters. Install [Go 1.26.x](https://go.dev/doc/install) before running the full local CI pipeline.
37+
3638
```shell
3739
# install required dependencies
3840
brew install git-lfs
3941
git lfs install
4042
git lfs pull
43+
go version
4144
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
4245
.~/.nvm/nvm.sh
4346
nvm install

‎package.json‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@
8686
"./projects/starters/bundles:ci",
8787
"./projects/starters/eleventy:ci",
8888
"./projects/starters/eleventy-ssr:ci",
89+
"./projects/starters/go:ci",
90+
"./projects/starters/go-htmx:ci",
8991
"./projects/starters/hugo:ci",
9092
"./projects/starters/importmaps:ci",
9193
"./projects/starters/lit-library:ci",

‎pnpm-lock.yaml‎

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎pnpm-workspace.yaml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ packages:
66
- projects/starters/eleventy
77
- projects/starters/eleventy-ssr
88
- projects/starters/go
9+
- projects/starters/go-htmx
910
- projects/starters/hugo
1011
- projects/starters/importmaps
1112
- projects/starters/lit-library

‎projects/internals/tools/src/project/service.test.ts‎

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ vi.mock('./setup-agent.js', () => ({
1515
vi.mock('./starters.js',()=>({
1616
startersData: {
1717
typescript: {cli: true,zip: 'typescript.zip'},
18-
react: {cli: true,zip: 'react.zip'},
19-
importmaps: {cli: false,zip: 'importmaps.zip'}
18+
go: {cli: true,zip: 'go.zip',setupDependencies: false},
19+
lit: {cli: false,zip: null}
2020
},
2121
createStarter: vi.fn(),
2222
startStarter: vi.fn()
@@ -60,6 +60,25 @@ describe('ProjectService', () => {
6060
expect(updateProject).toHaveBeenCalled();
6161
});
6262

63+
it('should skip dependency setup for starters that opt out of project setup',async()=>{
64+
const{ createStarter }=awaitimport('./starters.js');
65+
const{ setupAgent }=awaitimport('./setup-agent.js');
66+
const{ setupProject }=awaitimport('./setup.js');
67+
const{ updateProject }=awaitimport('./update.js');
68+
vi.mocked(createStarter).mockResolvedValue({create: {message: 'created',status: 'success'}});
69+
vi.mocked(setupAgent).mockResolvedValue({agent: {message: 'configured',status: 'success'}});
70+
71+
const{ ProjectService }=awaitimport('./service.js');
72+
constresult=awaitProjectService.create({type: 'go',cwd: '/test',start: false});
73+
74+
expect(result).toHaveProperty('create');
75+
expect(result).toHaveProperty('agent');
76+
expect(createStarter).toHaveBeenCalledWith('go','/test');
77+
expect(setupAgent).toHaveBeenCalled();
78+
expect(setupProject).not.toHaveBeenCalled();
79+
expect(updateProject).not.toHaveBeenCalled();
80+
});
81+
6382
it('should return failed report when a step fails',async()=>{
6483
const{ createStarter }=awaitimport('./starters.js');
6584
const{ setupAgent }=awaitimport('./setup-agent.js');

‎projects/internals/tools/src/project/service.ts‎

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ const starters = Object.keys(startersData).filter(
1616
starter=>startersData[starteraskeyoftypeofstartersData]?.cli
1717
)asStarter[];
1818

19+
functionstarterShouldSetupDependencies(type: Starter): boolean{
20+
conststarterData=startersData[type];
21+
return!('setupDependencies'instarterData)||starterData.setupDependencies;
22+
}
23+
1924
@service()
2025
exportclassProjectService{
2126
@tool({
@@ -56,9 +61,12 @@ export class ProjectService {
5661

5762
constcreateReport=awaitcreateStarter(type,dir);
5863
constagentReport=awaitsetupAgent(projectDir,'all');
59-
constsetupProjectReport=awaitsetupProject(projectDir);
60-
constupdateReport=awaitupdateProject(projectDir);
61-
constreports=[createReport,agentReport,setupProjectReport,updateReport];
64+
constreports=[createReport,agentReport];
65+
if(starterShouldSetupDependencies(type)){
66+
constsetupProjectReport=setupProject(projectDir);
67+
constupdateProjectReport=awaitupdateProject(projectDir);
68+
reports.push(setupProjectReport,updateProjectReport);
69+
}
6270

6371
constfailedReport=reports.find(report=>Object.values(report).some(value=>value.status==='danger'));
6472
if(failedReport){
@@ -69,12 +77,7 @@ export class ProjectService {
6977
awaitstartStarter(projectDir);
7078
}
7179

72-
return{
73-
...createReport,
74-
...agentReport,
75-
...setupProjectReport,
76-
...updateReport
77-
};
80+
returnObject.assign({}, ...reports);
7881
}
7982

8083
@tool({

‎projects/internals/tools/src/project/starters.test.ts‎

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import type * as childProcess from 'node:child_process';
55
import{beforeEach,describe,expect,it,vi}from'vitest';
66
import{
77
startersData,
8+
createStarterCDNUrl,
89
createGitInitProcess,
910
createStarterPaths,
1011
execPackageManager,
1112
getDependencyInstallFailureMessage,
1213
getRequiredNPMClient,
14+
stampStarterCDNVersions,
1315
removeWireitScripts,
1416
startStarter
1517
}from'./starters.js';
@@ -48,6 +50,7 @@ describe('startersData', () => {
4850
expect(startersData.nextjs.cli).toBe(true);
4951
expect(startersData.solidjs.cli).toBe(true);
5052
expect(startersData.go.cli).toBe(true);
53+
expect(startersData['go-htmx'].cli).toBe(true);
5154
expect(startersData.hugo.cli).toBe(true);
5255
expect(startersData.eleventy.cli).toBe(true);
5356
expect(startersData.bundles.cli).toBe(true);
@@ -66,6 +69,7 @@ describe('startersData', () => {
6669
expect(startersData.nextjs.zip).toContain('nextjs.zip');
6770
expect(startersData.solidjs.zip).toContain('solidjs.zip');
6871
expect(startersData.go.zip).toContain('go.zip');
72+
expect(startersData['go-htmx'].zip).toContain('go-htmx.zip');
6973
expect(startersData.eleventy.zip).toContain('eleventy.zip');
7074
expect(startersData.importmaps.zip).toContain('importmaps.zip');
7175
expect(startersData.bundles.zip).toContain('bundles.zip');
@@ -198,6 +202,96 @@ describe('getDependencyInstallFailureMessage', () => {
198202
});
199203
});
200204

205+
describe('starter CDN URLs',()=>{
206+
typeStarterCDNPackageName=Parameters<typeofcreateStarterCDNUrl>[0];
207+
typeStarterCDNAsset={packageName: StarterCDNPackageName;filePath: string};
208+
209+
constversions: Record<StarterCDNPackageName,string>={
210+
'@nvidia-elements/core': '1.2.3',
211+
'@nvidia-elements/styles': '4.5.6',
212+
'@nvidia-elements/themes': '7.8.9'
213+
};
214+
215+
constcoreAsset: StarterCDNAsset={packageName: '@nvidia-elements/core',filePath: 'dist/bundles/index.min.js'};
216+
conststylesAsset: StarterCDNAsset={packageName: '@nvidia-elements/styles',filePath: 'dist/bundles/index.css'};
217+
constthemesAsset: StarterCDNAsset={packageName: '@nvidia-elements/themes',filePath: 'dist/bundles/index.css'};
218+
constthemeFontsAsset: StarterCDNAsset={packageName: '@nvidia-elements/themes',filePath: 'dist/fonts/inter.css'};
219+
conststarterCDNAssets=[stylesAsset,themesAsset,themeFontsAsset,coreAsset];
220+
221+
functioncreateVersionedStarterCDNUrl(asset: StarterCDNAsset,version=versions[asset.packageName]){
222+
returncreateStarterCDNUrl(asset.packageName,version,asset.filePath);
223+
}
224+
225+
functioncreateUnversionedStarterCDNUrl(asset: StarterCDNAsset){
226+
returncreateVersionedStarterCDNUrl(asset).replace(
227+
`${asset.packageName}@${versions[asset.packageName]}`,
228+
asset.packageName
229+
);
230+
}
231+
232+
it('should create versioned CDN URLs',()=>{
233+
consturl=createVersionedStarterCDNUrl(coreAsset);
234+
235+
expect(newURL(url).protocol).toBe('https:');
236+
expect(url).toContain(`${coreAsset.packageName}@${versions[coreAsset.packageName]}/${coreAsset.filePath}`);
237+
});
238+
239+
it('should stamp unversioned Elements CDN URLs with package versions',()=>{
240+
constcontent=`
241+
<link rel="stylesheet" href="${createUnversionedStarterCDNUrl(stylesAsset)}" />
242+
<link rel="stylesheet" href="${createUnversionedStarterCDNUrl(themesAsset)}" />
243+
<link rel="stylesheet" href="${createUnversionedStarterCDNUrl(themeFontsAsset)}" />
244+
<script type="module">
245+
import '${createUnversionedStarterCDNUrl(coreAsset)}';
246+
</script>
247+
`;
248+
249+
constresult=stampStarterCDNVersions(content,versions);
250+
251+
for(constassetofstarterCDNAssets){
252+
expect(result).toContain(createVersionedStarterCDNUrl(asset));
253+
}
254+
});
255+
256+
it('should replace existing Elements CDN versions',()=>{
257+
constcontent=`
258+
<link rel="stylesheet" href="${createVersionedStarterCDNUrl(stylesAsset,'0.0.1')}" />
259+
<link rel="stylesheet" href="${createVersionedStarterCDNUrl(themesAsset,'0.0.1')}" />
260+
<link rel="stylesheet" href="${createVersionedStarterCDNUrl(themeFontsAsset,'0.0.1')}" />
261+
<script type="module">
262+
import '${createVersionedStarterCDNUrl(coreAsset,'0.0.1')}';
263+
</script>
264+
`;
265+
266+
constresult=stampStarterCDNVersions(content,versions);
267+
268+
for(constassetofstarterCDNAssets){
269+
expect(result).toContain(createVersionedStarterCDNUrl(asset));
270+
}
271+
expect(result).not.toContain('@0.0.1/');
272+
});
273+
274+
it('should leave unrelated CDN URLs unchanged',()=>{
275+
constunrelatedElementsPackageUrl=createVersionedStarterCDNUrl(coreAsset).replace(
276+
`${coreAsset.packageName}@${versions[coreAsset.packageName]}/${coreAsset.filePath}`,
277+
'@nvidia-elements/monaco/dist/bundles/index.css'
278+
);
279+
consthtmxUrl=createVersionedStarterCDNUrl(coreAsset).replace(
280+
`${coreAsset.packageName}@${versions[coreAsset.packageName]}/${coreAsset.filePath}`,
281+
'htmx.org@2.0.10/dist/htmx.min.js'
282+
);
283+
constcontent=`
284+
<link rel="stylesheet" href="${unrelatedElementsPackageUrl}" />
285+
<script src="${htmxUrl}"></script>
286+
`;
287+
288+
constresult=stampStarterCDNVersions(content,versions);
289+
290+
expect(result).toContain(unrelatedElementsPackageUrl);
291+
expect(result).toContain(htmxUrl);
292+
});
293+
});
294+
201295
describe('getNPMClient',()=>{
202296
it('should return the npm client',async()=>{
203297
expect(awaitgetNPMClient()).toBe('pnpm');

0 commit comments

Comments
 (0)