webresource

package module
v0.0.0-...-66ab2af Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 10, 2018 License: MIT Imports: 11 Imported by: 3

README

webresource proposal/prototype

JS/CSS/etc dependency prototype for Go

The webresource package is a prototype for how dependencies for browser libraries (JS, CSS and some others) could be implemented. See (https://github.com/golang/go/issues/25781) for discussion.

The objective is to make it possible to package JS/CSS/etc libraries as Go code and express their dependencies using the Go package dependency system (with particular attention to the transition toward semantic import versioning).

For example, this provides a means to "import bootstrap.js and thereby automatically get its dependency jquery.js". Vue.js and many, many other libraries could benefit from this same approach.

To make this idea viable, it would need to be standardized.

Usage:

your/app/main.go

package main

import "github.com/gocaveman/webresource"

import "github.com/gocaveman-libs/jquery"
import "github.com/gocaveman-libs/bootstrap"

func main() {

	modList := webresource.Resolve( // performs dependency resolution
		webresource.ModuleList{
			jquery.Module(), // in prototype: ...ModulePROTO()
			jslib.Module(),
		})
	// iterate overall all files in all modules in correct sequence, with dependencies
	modList.Walk(...)
}

github.com/twbs/bootstrap/webresource.go

package bootstrap

//go:generate mkwebresource -p "github.com/gocaveman-libs/bootstrap" -r "github.com/gocaveman-libs/jquery" .

The library maintainer can then use go generate which will invoke mkwebresource (currently at github.com/gocaveman/webresource/mkwebresource but presumably would go somewhere in golang.org/x) and package the JS and/or CSS files into a .go file (webresource-data.go by default). The -r option above specifies the packages this one depends on (which in turn result in import statements and cause bootstrap's Module().Requires() to return the jquery dependency.

The webresource.Resolve() function shown in main.go above walks the dependency tree and returns the modules in the correct order. And webresource.Walk() provides an easy way to iterate over all of the files of a specific type (file extension).

The above approach will work correctly with semantic import versioning also. (See concerns list below for caveats.)

What Problem This Addresses:

Go web applications often need to use JavaScript, CSS (and possibly other) files as part of their front-end/in-browser functionality. These JS and CSS files have very similar dependency management requirements to Go code itself, and yet there is currently no way for a JS or CSS library to appropriately package itself for use in a Go program.

Allowing JS and CSS libraries to present themselves to Go code as Go packages in a standardized way allows Go package management mechanism (today go get and various third party tools, but tomorrow vgo/go1.11+ and semantic import versioning) to be used to express these dependencies; and makes it easy for Go programs to depend on these libraries in a formalized way.

Making it easy for JS and CSS library vendors to drop a couple of files into their existing repository and make it usable as a Go package has high utility.

Working Demo Server:

The easist way to see this in action is to run the demo server:

go get github.com/gocaveman/webresource/demoserver
go install github.com/gocaveman/webresource/demoserver
$GOPATH/bin/demoserver

This shows an example web application which loads bootstrap and its dependencies.

Concerns:

Here's a roundup of the various concerns and my initial ideas on how to address them:

  • We don't specify how files are combined, minfied, or served, just exactly what is in Module.
  • The mkwebresource command line utility is there to make it easy to integrate into existing JS and CSS repos. Go generate functionality can also be used to invoke existing build processes (Grunt, etc.)
  • JS and CSS vendors won't adopt overnight, but a proxy repository can be made for a library and then when the original lib adopts, the proxy can be updated to just depend on the original. Things keep working.
  • This is intended for resources with very specific rules: a) must be usable in a browser (no server-side JS, no non-browser languages), b) must not reference local site URLs (e.g. no url(image.png)) as they cannot be relied on, c) must be applicable to an entire web page (JS and CSS are, for practical purposes "included on a page", assets like images are not and so in order to be usable must have a known URL, and become outside our scope), d) should not be directly derivable from one of the other input files (minified version, .map files, etc. - these operations should be done after, not included in the library)
  • Fonts can be supported the same was JS and CSS files can, they follow the above rules. There may be other types of resources that also follow the rules and these would be allowed.
  • This means langauges requiring transpilation are transpiled to JS beforehand (TypeScript, CoffeeScript). Same for SASS and LESS, they become CSS before we see them in a Module.
  • ES6+ should normally be transpiled to ES5, BUT it is not invalid for libraries which explicitly require a newer browser to use ES6, but they must be aware they are enforcing this decision on all libraries then depend on this one.
  • JS shims/polyfills should not be depended upon by libraries directly (this would result in bloat and probably duplication as multiple libraries use different polyfills to do the same thing). Instead libraries using newer features that might need polyfill should say that in the documentation and let the final application developer decide which polyfills to manually include to achieve what browser support.
  • JS libraries with various options can be expressed as each option being an individual package that depends on the core package. (Example: Syntax highlighter with core functionality and an option/module for each syntax it supports.)
  • All files must be UTF-8, we do not support other input encodings.
  • The Module interface is not compatible with other copies of it if moved to another package. The convention in the prototype is libraries define ModulePROTO(), if the webresource package were moved to golang.org/x as a sort of test run before bringing into stdlib (as was done with context), the convention could be ModuleX(), and finally when in stdlib just Module(). A module can support mulitple simulaneously without conflict.

Documentation

Overview

Dependency management for web resources like JS, CSS and other files.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Walk

func Walk(m Module, ext string, fn WalkFunc) error

Walk will visit each file in the FileSystem contained in this Module by calling the fn function. This does not walk Requires(). Sequence is determined by the underlying Readdir() calls.

Types

type FileSet

type FileSet struct {
	// contains filtered or unexported fields
}

FileSet implements Module and makes it easy to add file contents.

func NewFileSet

func NewFileSet(name string, requires ...Module) *FileSet

func (*FileSet) Mkdir

func (fs *FileSet) Mkdir(fullPath string, mode os.FileMode) *FileSet

Mkdir creates a directory. It must not already exists but its parents must, will panic otherwise. Readdir() will return files and directories in the sequence they are created.

func (*FileSet) MkdirAll

func (fs *FileSet) MkdirAll(fullPath string, mode os.FileMode) *FileSet

Mkdir creates a directory and all its parents. For directories that already exist this is a nop.

func (*FileSet) Name

func (fs *FileSet) Name() string

func (*FileSet) Open

func (fs *FileSet) Open(fullPath string) (http.File, error)

Open implements http.FileSystem.

func (*FileSet) Requires

func (fs *FileSet) Requires() []interface{}

func (*FileSet) String

func (fs *FileSet) String() string

func (*FileSet) WriteFile

func (fs *FileSet) WriteFile(fullPath string, mode os.FileMode, modTime time.Time, contents []byte) *FileSet

WriteFile creates a file. It must not already exists but its parents must, will panic otherwise. The modTime argument is included because it is useful to indicate the original build time of the file. Readdir() will return files and directories in the sequence they are created.

func (*FileSet) WriteGzipFile

func (fs *FileSet) WriteGzipFile(fullPath string, mode os.FileMode, modTime time.Time, contents []byte) *FileSet

WriteGzipFile works the same as WriteFile except it expects contents to be gzipped and will gunzip them when reading. Allows you to reduce the size of the in-memory and on-disk representation of this file.

type Module

type Module interface {
	http.FileSystem
	Name() string
	Requires() []interface{}
}

Module interface describes a webresource module with a name (matching the Go import path), an http.FileSystem with it's contents, and slice of other Modules that this one requires. By convention the Module instance for a particular package can be obtained by calling that package's top level Module() function.

type ModuleList

type ModuleList []Module

ModuleList is a Module slice with some useful methods.

func Resolve

func Resolve(r ModuleList) ModuleList

Resolve walks the dependency tree for the resources provided and returns a ModuleList in the correct sequence according to dependency rules. The order of the input list is not important, the same input set will always result in the same output.

func (ModuleList) Len

func (p ModuleList) Len() int

func (ModuleList) Less

func (p ModuleList) Less(i, j int) bool

Less sorts by Name()

func (ModuleList) Named

func (l ModuleList) Named(name string) Module

func (ModuleList) String

func (l ModuleList) String() string

Strings outputs the "%s" of each Module on its own line with the final newline trimmed.

func (ModuleList) Swap

func (p ModuleList) Swap(i, j int)

func (ModuleList) Walk

func (l ModuleList) Walk(ext string, fn WalkFunc) error

Walk will visit each file in each FileSystem contained in this Module by calling the fn function. This does not walk Requires(). Sequence is determined by the underlying Readdir() calls.

type WalkFunc

type WalkFunc func(m Module, fullPath string, f http.File) error

Directories

Path Synopsis
Demo of webresource - run a server with bootstrap and requirements.
Demo of webresource - run a server with bootstrap and requirements.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL