Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# These are supported funding model platforms

github: donseba # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
thanks_dev: # Replace with a single thanks.dev username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
56 changes: 56 additions & 0 deletions .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: golangci-lint
on:
push:
branches:
- master
- main
pull_request:

permissions:
contents: read
# Optional: allow read access to pull request. Use with `only-new-issues` option.
# pull-requests: read

jobs:
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '1.24'
cache: false
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
# Require: The version of golangci-lint to use.
# When `install-mode` is `binary` (default) the value can be v1.2 or v1.2.3 or `latest` to use the latest version.
# When `install-mode` is `goinstall` the value can be v1.2.3, `latest`, or the hash of a commit.
version: latest

# Optional: working directory, useful for monorepos
# working-directory: somedir

# Optional: golangci-lint command line arguments.
#
# Note: By default, the `.golangci.yml` file should be at the root of the repository.
# The location of the configuration file can be changed by using `--config=`
# args: --timeout=30m --config=/my/path/.golangci.yml --issues-exit-code=0
args: --out-format=colored-line-number

# Optional: show only new issues if it's a pull request. The default value is `false`.
# only-new-issues: true

# Optional: if set to true, then all caching functionality will be completely disabled,
# takes precedence over all other caching options.
# skip-cache: true

# Optional: if set to true, then the action won't cache or restore ~/go/pkg.
# skip-pkg-cache: true

# Optional: if set to true, then the action won't cache or restore ~/.cache/go-build.
# skip-build-cache: true

# Optional: The mode to install golangci-lint. It can be 'binary' or 'goinstall'.
# install-mode: "goinstall"
195 changes: 71 additions & 124 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,94 +1,84 @@
# go-importmap

ImportMap is a powerful Go package designed to simplify the management of JavaScript library dependencies for modern web applications. It facilitates the fetching, caching, and generation of import maps, allowing for seamless integration of JavaScript and CSS libraries directly into your projects. By abstracting the complexity of handling external library dependencies, ImportMap enables developers to focus on building feature-rich web applications without worrying about the underlying details of dependency management.
Features
go-importmap is a lightweight Go package for managing JavaScript and CSS dependencies. It fetches, caches, and generates import maps from popular CDNs, letting you focus on building your web applications.

- **Flexible Provider Interface**: Easily extendable to support different sources for fetching library packages, with a default implementation for cdnjs.
- **Automatic Caching**: Libraries are fetched and cached locally to improve load times and reduce external requests.
- **Import Map Generation**: Automatically generates import maps for included JavaScript and CSS files, adhering to the latest web standards.
- **Customizable Directories**: Configurable cache and assets directories to fit the structure of your project.
- **Shim Management**: Supports the inclusion of ES module shims for backward compatibility with non-module browsers.
- **Dynamic Package Management**: Add individual or multiple library packages with optional versioning and dependency requirements.
## Supported Providers
- **cdnjs**: Fetches library packages from the cdnjs CDN.
- **jsdelivr**: Fetches library packages from the jsDelivr CDN.
- **unpkg**: Fetches library packages from the unpkg CDN.
- **skypack**: Fetches library packages from the skypack CDN.
- **esm**: Fetches library packages from the esm.sh CDN.
- **Raw**: Fetches files from a custom URL.

## Getting Started
### Installation
## Features
- **Flexible Provider Interface**: Easily extendable to support multiple CDNs.
- **Automatic Caching**: Downloads and caches library files to boost performance.
- **Import Map Generation**: Automatically produces standards-compliant import maps.
- **Customizable Directories**: Configure cache and asset directories to suit your project.
- **Raw Imports**:Directly specify a URL to bypass the default provider.

To use ImportMap in your Go project, install it as a module:

## Installation

Install via Go modules:

```bash
go get github.com/donseba/go-importmap
```
### Usage Example
### Quick Example

Here's a quick example to get you started with ImportMap:

```go
package main

import (
"context"
"fmt"
"log"
"log/slog"
"context"
"fmt"
"log"

"github.com/donseba/go-importmap"
"github.com/donseba/go-importmap/library"
"github.com/donseba/go-importmap"
"github.com/donseba/go-importmap/library"
)

func main() {
ctx := context.TODO()

im := importmap.
NewDefaults().
WithLogger(slog.Default()).
ShimPath("https://ga.jspm.io/npm:es-module-shims@1.7.0")

im.WithPackages([]library.Package{
{
Name: "htmx",
Version: "1.8.5", // locking a specific version
Require: []library.Include{
{
File: "htmx.min.js",
},
{
File: "/ext/json-enc.js",
As: "json-enc",
},
},
},
{
Name: "bootstrap",
Require: []library.Include{
{
File: "css/bootstrap.min.css",
As: "bootstrap",
},
{
File: "js/bootstrap.min.js",
As: "bootstrap",
},
},
},
})

// retrieve all libraries
err := im.Fetch(ctx)
if err != nil {
log.Fatal(err)
return
}

// render the html block including script tags.
tmpl, err := im.Render()
if err != nil {
log.Fatal(err)
return
}

fmt.Println(tmpl)
ctx := context.TODO()

im := importmap.
NewDefaults().
WithPackages([]library.Package{
{
Name: "htmx",
Version: "1.9.10",
Require: []library.Include{
{ File: "htmx.min.js" },
{ File: "/ext/json-enc.js", As: "json-enc" },
},
},
{
Name: "bootstrap",
Require: []library.Include{
{ File: "css/bootstrap.min.css" },
{ File: "js/bootstrap.min.js", As: "bootstrap" },
},
},
})

if err := im.Fetch(ctx); err != nil {
log.Fatal(err)
}

tmpl, err := im.Render()
if err != nil {
log.Fatal(err)
}

fmt.Println(tmpl)
}
```
This code initializes ImportMap with default settings, fetches the specified libraries from your chosen provider, and generates an HTML snippet containing the necessary script and link tags.


Resulting in the following output:
```html
<link rel="stylesheet" href="/assets/css/bootstrap.min.css" as="bootstrap"/>
Expand All @@ -111,75 +101,32 @@ Finally, it renders the HTML block with the necessary script tags for the librar

ImportMap offers several methods to customize its behavior according to your project's needs:

- **WithDefaults()**: Initializes ImportMap with default settings for cache and assets directories, and the ES module shim.
- **WithProvider(provider Provider)**: Sets a custom provider for fetching library files.
- **WithPackages(packages []library.Package)**: Sets multiple library packages to the import map.
- **WithDefaults()**: Initialize with sensible defaults for cache and asset directories.
- **WithProvider(provider Provider)**: Set a custom provider for fetching library files.
- **WithPackages(packages []library.Package)**: Add one or more library packages.
- **WithPackage(package library.Package)**: Adds a single library package to the import map.
- **AssetsDir(dir string)**: Sets the directory path for assets, default is `assets`.
- **CacheDir(dir string)**: Sets the directory path for the cache, default is `.importmap`.
- **RootDir(dir string)**: Sets the directory paths for assets, cache, and root directories, respectively.
- **ShimPath(sp string)**: Sets the path to the ES module shim, default is `https://ga.jspm.io/npm:es-module-shims@1.7.0/dist/es-module-shims.js`.


## file structure

```
- .importmap
- bootstrap
- 5.1.3
- css
- bootstrap.css
- bootstrap.min.css
- bootstrap-grid.css
- bootstrap-grid.min.css
- js
- bootstrap.js
- bootstrap.bundle.js
- bootstrap.min.js
- htmx
- 1.8.5
- htmx.min.js
- ext
- json-enc.js
- ajax-header.js
- apline-morphd.js
- ... etc
- assets
- bootstrap
-5.1.3
- css
- bootstrap.min.css
- js
- bootstrap.min.js
- htmx
- 1.8.5
- htmx.min.js
- ext
- json-enc.js
```

As you can see the `.importmap` contains all the files fetched from the cdnjs, while the `assets` contains the files that are used in the importmap.
- **ShimPath(sp string)**:Specify the ES module shim URL.

## RAW Imports

it is possible to bypass the cdnjs by using the `Raw` param on the package.
it is possible to bypass the cdnjs by using the using the Raw provider:

```go
pr := cdnjs.New()
im := New().WithProvider(pr).WithLogger(slog.Default())

im.WithPackages([]library.Package{
{
Name: "htmx",
Version: "1.8.6",
Require: []library.Include{
{
Raw: "https://unpkg.com/browse/htmx.org@1.8.6/dist/htmx.min.js",
As: "htmx",
},
},
Name: "htmx",
Version: "2.0.4",
Provider: raw.New("https://unpkg.com/browse/htmx.org@2.0.4/dist/htmx.min.js"),
},
})
```

This wil generate:
results in generating:
```json
{"imports":{"htmx":"https://unpkg.com/browse/htmx.org@1.8.6/dist/htmx.min.js"}}
```
Expand Down
6 changes: 3 additions & 3 deletions client/cdnjs/cdnjs.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ package cdnjs
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/donseba/go-importmap/library"
"net/http"

"github.com/donseba/go-importmap/library"
)

var (
Expand Down Expand Up @@ -57,7 +57,7 @@ func (c *Client) FetchPackageFiles(ctx context.Context, name, version string) (l
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return nil, "", errors.New(fmt.Sprintf("client api responded with code %d", resp.StatusCode))
return nil, "", fmt.Errorf("client api responded with code %d", resp.StatusCode)
}

var sr SearchResponse
Expand Down
4 changes: 1 addition & 3 deletions client/cdnjs/cdnjs_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package cdnjs

import (
"context"
"fmt"
"testing"
)

func TestClient_Search(t *testing.T) {
ctx := context.TODO()
cs := New()

var tests = []struct {
Expand All @@ -23,7 +21,7 @@ func TestClient_Search(t *testing.T) {
for _, tt := range tests {
testName := fmt.Sprintf("%s,%s,%s", tt.name, tt.version, tt.filename)
t.Run(testName, func(t *testing.T) {
p, _, err := cs.FetchPackageFiles(ctx, tt.name, tt.version)
p, _, err := cs.FetchPackageFiles(t.Context(), tt.name, tt.version)
if err != nil {
t.Error(err)
}
Expand Down
Loading