chore(6.0): remove concatjs package It should move to https://github.com/angular/dev-infra which is the only consumer that has resources to maintain it.
diff --git a/internal/js_library/js_library.bzl b/internal/js_library/js_library.bzl index d387f94..96e962c 100644 --- a/internal/js_library/js_library.bzl +++ b/internal/js_library/js_library.bzl
@@ -56,7 +56,7 @@ "named_module_srcs": attr.label_list( doc = """Non-public legacy API, not recommended to make new usages. A subset of srcs that are javascript named-UMD or - named-AMD for use in rules such as concatjs_devserver. + named-AMD. They will be copied into the package bin folder if needed.""", allow_files = True, ), @@ -350,10 +350,6 @@ ) ``` - > To help work with "named AMD" modules as required by `concatjs_devserver` and other Google-style "concatjs" rules, - > `js_library` has some undocumented advanced features you can find in the source code or in our examples. - > These should not be considered a public API and aren't subject to our usual support and semver guarantees. - ### Outputs Like all Bazel rules it produces a default output by providing [DefaultInfo].
diff --git a/internal/providers/js_providers.bzl b/internal/providers/js_providers.bzl index a9c3b71..c6e9a9d 100644 --- a/internal/providers/js_providers.bzl +++ b/internal/providers/js_providers.bzl
@@ -40,7 +40,6 @@ doc = """JavaScript files whose module name is self-contained. For example named AMD/UMD or goog.module format. -These files can be efficiently served with the concatjs bundler. These outputs should be named "foo.umd.js" (note that renaming it from "foo.js" doesn't affect the module id)
diff --git a/packages/concatjs/BUILD.bazel b/packages/concatjs/BUILD.bazel deleted file mode 100644 index 28bc65b..0000000 --- a/packages/concatjs/BUILD.bazel +++ /dev/null
@@ -1,112 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -load("replacements.bzl", "TYPESCRIPT_REPLACEMENTS") -load("@bazel_skylib//:bzl_library.bzl", "bzl_library") -load("@build_bazel_rules_nodejs//:index.bzl", "js_library") -load("@build_bazel_rules_nodejs//:tools/defaults.bzl", "pkg_npm") -load("@build_bazel_rules_nodejs//packages/typescript:index.bzl", "ts_project") -load("@build_bazel_rules_nodejs//tools/stardoc:index.bzl", "stardoc") -load("//third_party/github.com/bazelbuild/bazel-skylib:rules/copy_file.bzl", "copy_file") - -ts_project( - name = "bazel_concatjs_lib", - srcs = glob(["*.ts"]), - tsconfig = { - "compilerOptions": { - "declaration": True, - "module": "umd", - "moduleResolution": "node", - "types": ["node"], - }, - }, - deps = [ - "@npm//@types/node", - "@npm//karma", - ], -) - -js_library( - name = "bazel_concatjs", - package_name = "@bazel/concatjs", - srcs = ["bazel_concatjs_lib"], - visibility = ["//packages/concatjs:__subpackages__"], -) - -bzl_library( - name = "bzl", - testonly = True, - srcs = glob(["*.bzl"]), - deps = [ - "//packages/concatjs/devserver:bzl", - "//packages/concatjs/internal:bzl", - "//packages/concatjs/web_test:bzl", - "@bazel_skylib//lib:types", - "@build_bazel_rules_nodejs//:bzl", - "@build_bazel_rules_nodejs//internal/common:bzl", - "@build_bazel_rules_nodejs//internal/node:bzl", - "@rules_nodejs//nodejs:bzl", - ], -) - -stardoc( - name = "docs", - testonly = True, - out = "README.md_", - input = "index.docs.bzl", - tags = ["fix-windows"], - deps = [":bzl"], -) - -genrule( - name = "docs_scrub_platform", - srcs = ["README.md_"], - outs = ["README.md"], - cmd = "sed -e 's#devserver:devserver_.*_a[mr][dm]64#devserver:devserver_[platform]#' <$< >$@", - visibility = ["//docs:__pkg__"], -) - -copy_file( - name = "npm_version_check", - src = "//internal:npm_version_check.js", - out = ":npm_version_check.js", -) - -pkg_npm( - name = "npm_package", - package_name = "@bazel/concatjs", - srcs = [ - "index.bzl", - "package.json", - "//packages/concatjs/devserver:package_contents", - "//packages/concatjs/internal:package_contents", - "//packages/concatjs/third_party/github.com/bazelbuild/bazel/src/main/protobuf:npm_package_assets", - "//packages/concatjs/third_party/npm/requirejs:package_contents", - "//packages/concatjs/web_test:package_contents", - ], - build_file_content = "", - substitutions = TYPESCRIPT_REPLACEMENTS, - deps = [ - ":README.md", - ":bazel_concatjs_lib", - ":npm_version_check", - "//packages/concatjs/devserver:devserver-darwin", - "//packages/concatjs/devserver:devserver-darwin_arm64", - "//packages/concatjs/devserver:devserver-linux", - "//packages/concatjs/devserver:devserver-windows", - "//packages/concatjs/internal:BUILD", - "//packages/concatjs/internal:generated_BUILD", - "//packages/concatjs/internal:tsc_wrapped", - ], -)
diff --git a/packages/concatjs/devserver/BUILD.bazel b/packages/concatjs/devserver/BUILD.bazel deleted file mode 100644 index d0b267d..0000000 --- a/packages/concatjs/devserver/BUILD.bazel +++ /dev/null
@@ -1,234 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -load("@bazel_skylib//:bzl_library.bzl", "bzl_library") - -# BEGIN-INTERNAL -load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") - -# END-INTERNAL -licenses(["notice"]) # Apache 2.0 - -package(default_visibility = [ - "//packages/concatjs:__subpackages__", -]) - -bzl_library( - name = "bzl", - srcs = glob(["*.bzl"]), - deps = [ - "@build_bazel_rules_nodejs//internal/js_library:bzl", - "@build_bazel_rules_nodejs//internal/pkg_web:bzl", - ], -) - -exports_files(["launcher_template.sh"]) - -filegroup( - name = "devserver_darwin_amd64", - srcs = ["devserver-darwin_x64"], - # Don't build on CI - tags = ["manual"], - visibility = ["//visibility:public"], -) - -filegroup( - name = "devserver_linux_amd64", - srcs = ["devserver-linux_x64"], - # Don't build on CI - tags = ["manual"], - visibility = ["//visibility:public"], -) - -filegroup( - name = "devserver_linux_arm64", - srcs = ["devserver-linux_arm64"], - # Don't build on CI - tags = ["manual"], - visibility = ["//visibility:public"], -) - -filegroup( - name = "devserver_linux_s390x", - srcs = ["devserver-linux_s390x"], - # Don't build on CI - tags = ["manual"], - visibility = ["//visibility:public"], -) - -filegroup( - name = "devserver_windows_amd64", - srcs = ["devserver-windows_x64.exe"], - # Don't build on CI - tags = ["manual"], - visibility = ["//visibility:public"], -) - -filegroup( - name = "devserver_linux_ppc64le", - srcs = ["devserver-linux_ppc64le"], - # Don't build on CI - tags = ["manual"], - visibility = ["//visibility:public"], -) - -filegroup( - name = "devserver_darwin_arm64", - srcs = ["devserver-darwin_arm64"], - # Don't build on CI - tags = ["manual"], - visibility = ["//visibility:public"], -) - -config_setting( - name = "darwin_x64", - constraint_values = [ - "@platforms//os:osx", - "@platforms//cpu:x86_64", - ], -) - -config_setting( - name = "darwin_arm64", - constraint_values = [ - "@platforms//os:osx", - "@platforms//cpu:arm64", - ], -) - -config_setting( - name = "linux_x64", - constraint_values = [ - "@platforms//os:linux", - "@platforms//cpu:x86_64", - ], -) - -config_setting( - name = "linux_arm64", - constraint_values = [ - "@platforms//os:linux", - "@platforms//cpu:arm64", - ], -) - -config_setting( - name = "linux_s390x", - constraint_values = [ - "@platforms//os:linux", - "@platforms//cpu:s390x", - ], -) - -config_setting( - name = "windows_x64", - constraint_values = [ - "@platforms//os:windows", - "@platforms//cpu:x86_64", - ], -) - -config_setting( - name = "linux_ppc64le", - constraint_values = [ - "@platforms//os:linux", - "@platforms//cpu:ppc", - ], -) - -filegroup( - name = "devserver", - srcs = select({ - ":darwin_arm64": [":devserver_darwin_arm64"], - ":darwin_x64": [":devserver_darwin_amd64"], - ":linux_arm64": [":devserver_linux_arm64"], - ":linux_s390x": [":devserver_linux_s390x"], - ":linux_x64": [":devserver_linux_amd64"], - ":windows_x64": [":devserver_windows_amd64"], - ":linux_ppc64le": [":devserver_linux_ppc64le"], - }), - # Don't build on CI - tags = ["manual"], - visibility = ["//visibility:public"], -) - -filegroup( - name = "package_contents", - srcs = glob(["*"]), - visibility = ["//packages/concatjs:__pkg__"], -) - -# BEGIN-INTERNAL -go_library( - name = "go_default_library", - srcs = [ - "main.go", - "runfile-filesystem.go", - ], - importpath = "github.com/bazelbuild/rules_typescript/devserver", - visibility = ["//visibility:private"], - deps = [ - "//packages/concatjs/devserver/concatjs:go_default_library", - "//packages/concatjs/devserver/devserver:go_default_library", - "//packages/concatjs/devserver/runfiles:go_default_library", - ], -) - -go_binary( - name = "devserver_bin", - embed = [":go_default_library"], - visibility = ["//visibility:public"], -) - -go_binary( - name = "devserver-darwin", - out = "devserver-darwin_x64", - embed = [":go_default_library"], - goarch = "amd64", - goos = "darwin", - pure = "on", - visibility = ["//visibility:public"], -) - -go_binary( - name = "devserver-linux", - out = "devserver-linux_x64", - embed = [":go_default_library"], - goarch = "amd64", - goos = "linux", - pure = "on", - visibility = ["//visibility:public"], -) - -go_binary( - name = "devserver-windows", - out = "devserver-windows_x64.exe", - embed = [":go_default_library"], - goarch = "amd64", - goos = "windows", - pure = "on", - visibility = ["//visibility:public"], -) - -go_binary( - name = "devserver-darwin_arm64", - out = "devserver-darwin_arm64", - embed = [":go_default_library"], - goarch = "arm64", - goos = "darwin", - pure = "on", - visibility = ["//visibility:public"], -) - -# END-INTERNAL
diff --git a/packages/concatjs/devserver/concatjs/BUILD.bazel b/packages/concatjs/devserver/concatjs/BUILD.bazel deleted file mode 100644 index 6cc1a74..0000000 --- a/packages/concatjs/devserver/concatjs/BUILD.bazel +++ /dev/null
@@ -1,14 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "go_default_library", - srcs = ["concatjs.go"], - importpath = "github.com/bazelbuild/rules_typescript/devserver/concatjs", - visibility = ["//visibility:public"], -) - -go_test( - name = "go_default_test", - srcs = ["concatjs_test.go"], - embed = [":go_default_library"], -)
diff --git a/packages/concatjs/devserver/concatjs/concatjs.go b/packages/concatjs/devserver/concatjs/concatjs.go deleted file mode 100644 index bde0244..0000000 --- a/packages/concatjs/devserver/concatjs/concatjs.go +++ /dev/null
@@ -1,361 +0,0 @@ -// Package concatjs provides a simple way of serving JavaScript sources in development. -package concatjs - -import ( - "bufio" - "bytes" - "compress/gzip" - "fmt" - "io" - "io/ioutil" - "log" - "net/http" - "os" - "path/filepath" - "regexp" - "strings" - "sync" - "time" -) - -// ServeConcatenatedJS returns an http.Handler that serves the JavaScript files -// listed in manifestPath in one concatenated, eval separated response body. -// -// This greatly speeds up development load times due to fewer HTTP requests, but -// still for easy debugging by giving the eval'ed fragments URLs through -// sourceURL comments. -// -// Example usage: -// http.Handle("/app_combined.js", -// concatjs.ServeConcatenatedJS("my/app/web_srcs.MF", ".", [], [], nil)) -// -// Relative paths in the manifest are resolved relative to the path given as root. -func ServeConcatenatedJS(manifestPath string, root string, preScripts []string, postScripts []string, fs FileSystem) http.Handler { - var lock sync.Mutex // Guards cache. - cache := NewFileCache(root, fs) - - manifestPath = filepath.Join(root, manifestPath) - - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/javascript; charset=utf-8") - files, err := manifestFiles(manifestPath) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - writeJSError(w, "Failed to read manifest: %v", err) - return - } - var writer io.Writer = w - if acceptGzip(r.Header) { - // NB: gzip is not supported in App Engine, as the header is stripped: - // https://cloud.google.com/appengine/docs/go/requests#Go_Request_headers - // CompressionLevel = 3 is a reasonable compromise between speed and compression. - gzw, err := gzip.NewWriterLevel(w, 3) - if err != nil { - log.Fatalf("Could not create gzip writer: %s", err) - } - defer gzw.Close() - writer = gzw - w.Header().Set("Content-Encoding", "gzip") - } - - // Write out pre scripts - for _, s := range preScripts { - fmt.Fprint(writer, s) - // Ensure scripts are separated by a newline - fmt.Fprint(writer, "\n") - } - - // Protect the cache with a lock because it's possible for multiple requests - // to be handled in parallel. - lock.Lock() - cache.WriteFiles(writer, files) - lock.Unlock() - - // Write out post scripts - for _, s := range postScripts { - fmt.Fprint(writer, s) - // Ensure scripts are separated by a newline - fmt.Fprint(writer, "\n") - } - }) -} - -var acceptHeader = http.CanonicalHeaderKey("Accept-Encoding") - -func acceptGzip(h http.Header) bool { - for _, hv := range h[acceptHeader] { - for _, enc := range strings.Split(hv, ",") { - if strings.TrimSpace(enc) == "gzip" { - return true - } - } - } - return false -} - -// FileSystem is the interface to reading files from disk. -// It's abstracted into an interface to allow tests to replace it. -type FileSystem interface { - StatMtime(filename string) (time.Time, error) - ReadFile(filename string) ([]byte, error) - ResolvePath(root string, file string) (string, error) -} - -// RealFileSystem implements FileSystem by actual disk access. -type RealFileSystem struct{} - -// StatMtime gets the last modification time of the specified file. -func (fs *RealFileSystem) StatMtime(filename string) (time.Time, error) { - s, err := os.Stat(filename) - if err != nil { - return time.Time{}, err - } - return s.ModTime(), nil -} - -// ReadFile reads the specified file using the real filesystem. -func (fs *RealFileSystem) ReadFile(filename string) ([]byte, error) { - return ioutil.ReadFile(filename) -} - -// ResolvePath resolves the specified path within a given root by joining root and the filepath. -// This is only works if the specified file is located within the given root in the -// real filesystem. This does not work in Bazel where requested files aren't always -// located within the specified root. Files would need to be resolved as runfiles. -func (fs *RealFileSystem) ResolvePath(root string, file string) (string, error) { - return filepath.Join(root, file), nil -} - -// FileCache caches a set of files in memory and provides a single -// method, WriteFiles(), that streams them out in the concatjs format. -type FileCache struct { - fs FileSystem - root string - - entries map[string]*cacheEntry -} - -// NewFileCache constructs a new FileCache. Relative paths in the cache -// are resolved relative to root. fs injects file system access, and -// will use the real file system if nil. -func NewFileCache(root string, fs FileSystem) *FileCache { - if fs == nil { - fs = &RealFileSystem{} - } - return &FileCache{ - root: root, - fs: fs, - entries: map[string]*cacheEntry{}, - } -} - -type cacheEntry struct { - // err holds an error encountered while updating the entry; if - // it's non-nil, then mtime, contents and the resolved path are invalid. - err error - mtime time.Time - contents []byte - resolvedPath string -} - -// manifestFiles parses a manifest, returning a list of the files in the manifest. -// It skips blank lines and javascript/closure/deps.js. -func manifestFiles(manifest string) ([]string, error) { - f, err := os.Open(manifest) - if err != nil { - return nil, fmt.Errorf("could not read manifest %s: %s", manifest, err) - } - defer f.Close() - return manifestFilesFromReader(f) -} - -// manifestFilesFromReader is a helper for manifestFiles, split out for testing. -func manifestFilesFromReader(r io.Reader) ([]string, error) { - var lines []string - s := bufio.NewScanner(r) - for s.Scan() { - path := s.Text() - if path == "" { - continue - } - if path == "javascript/closure/deps.js" { - // Ignore/skip deps.js, it is unused due to CLOSURE_NO_DEPS = true and superseded by the - // dependency handling in this file. It's harmless, but a large download (>450 KB). - continue - } - lines = append(lines, path) - } - if err := s.Err(); err != nil { - return nil, err - } - - return lines, nil -} - -// writeJSError writes an error both to the log and into w as a JavaScript throw statement. -func writeJSError(w io.Writer, format string, a ...interface{}) { - log.Printf(format, a...) - fmt.Fprint(w, "throw new Error('") - fmt.Fprintf(w, format, a...) - fmt.Fprint(w, "');\n") -} - -// WriteFiles updates the cache for a list of files, then streams them into an io.Writer. -func (cache *FileCache) WriteFiles(w io.Writer, files []string) error { - // Ensure the cache is up to date with respect to the on-disk state. - // Note that refreshFiles cannot fail; any errors encountering while refreshing - // are stored in the cache entry and streamed into the response. - cache.refreshFiles(files) - - for _, path := range files { - if _, err := fmt.Fprintf(w, "// %s\n", path); err != nil { - return err - } - ce := cache.entries[path] - if ce.err != nil { - writeJSError(w, "loading %s failed: %s", path, ce.err) - continue - } - if _, err := w.Write(ce.contents); err != nil { - return err - } - } - return nil -} - -// refresh ensures a single cacheEntry is up to date. It stat()s and -// potentially reads the contents of the file it is caching. -func (e *cacheEntry) refresh(fs FileSystem) error { - mt, err := fs.StatMtime(e.resolvedPath) - if err != nil { - return err - } - if e.mtime == mt && e.contents != nil { - return nil // up to date - } - - contents, err := fileContents(e.resolvedPath, fs) - if err != nil { - return err - } - e.mtime = mt - e.contents = contents - return nil -} - -// Convert Windows paths separators. We can use this to create canonical paths that -// can be also used as browser source urls. -var pathReplacer = strings.NewReplacer("\\", "/") - -// refreshFiles stats the given files and updates the cache for them. -func (cache *FileCache) refreshFiles(files []string) { - // Stating many files asynchronously is faster on network file systems. - // Push all files that need to be stat'd into a channel and have - // a set of workers stat/read them to update the cache entry. - type workItem struct { - path string - entry *cacheEntry - } - work := make(chan workItem) - - var wg sync.WaitGroup - wg.Add(len(files)) - for i := 0; i < len(files); i++ { - // TODO(evanm): benchmark limiting this to fewer goroutines. - go func() { - w := <-work - w.entry.err = w.entry.refresh(cache.fs) - wg.Done() - }() - } - - for _, path := range files { - entry := cache.entries[path] - if entry == nil { - // Resolve path only once for a cache entry. The resolved path will be part of the - // cache item. - resolvedPath, err := cache.fs.ResolvePath(cache.root, path) - - if err != nil { - fmt.Fprintf(os.Stderr, "could not resolve path %s. %v\n", path, err) - os.Exit(1) - } - - // Create a new cache entry with the corresponding resolved path. Also normalize the path - // before storing it persistently in the cache. The normalizing is good to do here because - // the path might be used in browser source URLs and should be kept in posix format. - entry = &cacheEntry{ - resolvedPath: pathReplacer.Replace(resolvedPath), - } - cache.entries[path] = entry - } - work <- workItem{path, entry} - } - close(work) - - wg.Wait() -} - -// The maximum number of bytes of a source file to be searched for the "goog.module" declaration. -// Limited to 50,000 bytes to avoid degenerated performance on large compiled JS (e.g. a -// pre-compiled AngularJS binary). -const googModuleSearchLimit = 50 * 1000 - -// Matches files containing "goog.module", which have to be served slightly differently. -var googModuleRegExp = regexp.MustCompile(`(?m)^\s*goog\.module\s*\(\s*['"]`) - -// fileContents returns escaped JS file contents for the given path. -func fileContents(path string, fs FileSystem) ([]byte, error) { - contents, err := fs.ReadFile(path) - if err != nil { - return nil, err - } - var f bytes.Buffer - // goog.module files must be wrapped in a goog.loadModule call. Check the first X bytes of the file for it. - limit := googModuleSearchLimit - if len(contents) < limit { - limit = len(contents) - } - if googModuleRegExp.Match(contents[:limit]) { - fmt.Fprint(&f, "goog.loadModule('") - } else { - fmt.Fprint(&f, "eval('") - } - if err := writeJSEscaped(&f, contents); err != nil { - log.Printf("Failed to write file contents of %s: %s", path, err) - return nil, err - } - fmt.Fprintf(&f, "\\n\\n//# sourceURL=http://concatjs/%s\\n');\n", path) - - return f.Bytes(), nil -} - -// writeJSEscaped writes contents into the given writer, escaping for content in -// a single quoted JavaScript string. -func writeJSEscaped(out io.Writer, contents []byte) error { - // template.JSEscape escapes whitespace and line breaks to bulky six-character - // escapes, substantially blowing up response size, and is also a bit slower. - // As this also doesn't need safe escaping, this code just rather escapes itself. - for _, b := range contents { - switch b { - case '\n': - if _, err := out.Write([]byte("\\n")); err != nil { - return err - } - case '\r': - if _, err := out.Write([]byte("\\r")); err != nil { - return err - } - case '\\', '\'': - if _, err := out.Write([]byte{'\\'}); err != nil { - return err - } - fallthrough - default: - if _, err := out.Write([]byte{b}); err != nil { - return err - } - } - } - return nil -}
diff --git a/packages/concatjs/devserver/concatjs/concatjs_test.go b/packages/concatjs/devserver/concatjs/concatjs_test.go deleted file mode 100644 index ddd1a47..0000000 --- a/packages/concatjs/devserver/concatjs/concatjs_test.go +++ /dev/null
@@ -1,217 +0,0 @@ -package concatjs - -import ( - "bytes" - "fmt" - "net/http" - "net/http/httptest" - "path/filepath" - "reflect" - "strings" - "sync" - "testing" - "time" -) - -// This test is mostly verifying that we drop javascript/closure/deps.js -// This is only important in google3. -func TestManifestFiles(t *testing.T) { - files, err := manifestFilesFromReader(strings.NewReader(`foo.js - -javascript/closure/deps.js -bar.js -`)) - if err != nil { - t.Fatal(err) - } - want := []string{"foo.js", "bar.js"} - if !reflect.DeepEqual(files, want) { - t.Errorf("Parse incorrect, got %v, want %v", files, want) - } -} - -func TestWriteJSEscaped(t *testing.T) { - var b bytes.Buffer - if err := writeJSEscaped(&b, []byte("test \\ ' \n \r end")); err != nil { - t.Error(err) - } - got := string(b.Bytes()) - want := `test \\ \' \n \r end` - if got != want { - t.Errorf("Incorrect escaping, want %s, got %s", want, got) - } -} - -type fakeFileSystem struct { - mux sync.Mutex - fakeReadFile func(filename string) ([]byte, error) - fakeStatMtime func(filename string) (time.Time, error) - fakeResolvePath func(root string, filename string) (string, error) -} - -func (fs *fakeFileSystem) ReadFile(filename string) ([]byte, error) { - fs.mux.Lock() - defer fs.mux.Unlock() - return fs.fakeReadFile(filename) -} - -func (fs *fakeFileSystem) StatMtime(filename string) (time.Time, error) { - fs.mux.Lock() - defer fs.mux.Unlock() - return fs.fakeStatMtime(filename) -} - -func (fs *fakeFileSystem) ResolvePath(root string, filename string) (string, error) { - fs.mux.Lock() - defer fs.mux.Unlock() - return fs.fakeResolvePath(root, filename) -} - -func TestWriteFiles(t *testing.T) { - var inputFiles = []string{"a", "missing", "module"} - - fs := fakeFileSystem{ - fakeReadFile: func(filename string) ([]byte, error) { - var normalizedFilename = pathReplacer.Replace(filename) - switch normalizedFilename { - case "root/a": - return []byte("a content"), nil - case "root/module": - return []byte("// A module\ngoog.module('hello');"), nil - default: - return []byte{}, fmt.Errorf("unexpected file read: %s", normalizedFilename) - } - }, - fakeStatMtime: func(filename string) (time.Time, error) { - var normalizedFilename = pathReplacer.Replace(filename) - switch normalizedFilename { - case "root/a", "root/module": - return time.Now(), nil - default: - return time.Time{}, fmt.Errorf("unexpected file stat: %s", normalizedFilename) - } - }, - fakeResolvePath: func(root string, filename string) (string, error) { - return filepath.Join(root, filename), nil - }, - } - - cache := NewFileCache("root", &fs) - - var b bytes.Buffer - cache.WriteFiles(&b, inputFiles) - - got := string(b.Bytes()) - want := `// a -eval('a content\n\n//# sourceURL=http://concatjs/root/a\n'); -// missing -throw new Error('loading missing failed: unexpected file stat: root/missing'); -// module -goog.loadModule('// A module\ngoog.module(\'hello\');\n\n//# sourceURL=http://concatjs/root/module\n'); -` - - if got != want { - t.Errorf("Response differs, want %s, got %s", want, got) - } -} - -func TestFileCaching(t *testing.T) { - var reads int - - fs := fakeFileSystem{ - fakeReadFile: func(string) ([]byte, error) { - reads++ - return nil, nil - }, - fakeStatMtime: func(string) (time.Time, error) { - return time.Time{}, nil - }, - fakeResolvePath: func(root string, filename string) (string, error) { - return filepath.Join(root, filename), nil - }, - } - - var b bytes.Buffer - cache := NewFileCache("", &fs) - cache.WriteFiles(&b, []string{"a", "b"}) - if reads != 2 { - t.Errorf("got %d file reads, want 2", reads) - } - reads = 0 - cache.WriteFiles(&b, []string{"a", "b"}) - if reads != 0 { - t.Errorf("got %d reads, expected no further", reads) - } -} - -func TestAcceptHeader(t *testing.T) { - tests := []struct { - header map[string][]string - expected bool - }{ - {header: map[string][]string{"Other": []string{"gzip"}}, expected: false}, - {header: map[string][]string{"Accept-Encoding": []string{"rot13"}}, expected: false}, - {header: map[string][]string{"Accept-Encoding": []string{"rot13, gzip, deflate"}}, expected: true}, - } - for _, test := range tests { - res := acceptGzip(test.header) - if res != test.expected { - t.Errorf("Expect %t, got %t for %s", test.expected, res, test.header) - } - } -} - -func TestCustomFileResolving(t *testing.T) { - fs := fakeFileSystem{ - fakeReadFile: func(filename string) ([]byte, error) { - var normalizedFilename = pathReplacer.Replace(filename) - switch normalizedFilename { - case "/system_root/bazel-bin/a.txt": - return []byte("a content"), nil - case "/system_root/bazel-bin/nested/b.js": - return []byte("b content"), nil - default: - return []byte{}, fmt.Errorf("unexpected file read: %s", normalizedFilename) - } - }, - fakeStatMtime: func(filename string) (time.Time, error) { - return time.Now(), nil - }, - fakeResolvePath: func(root string, filename string) (string, error) { - // For this test, we use an absolute root. This is similar to how - // Bazel resolves runfiles through the manifest. - return filepath.Join("/system_root/bazel-bin/", filename), nil - }, - } - - cache := NewFileCache("", &fs) - - var b bytes.Buffer - cache.WriteFiles(&b, []string{"a.txt", "nested/b.js"}) - - actual := string(b.Bytes()) - expected := `// a.txt -eval('a content\n\n//# sourceURL=http://concatjs//system_root/bazel-bin/a.txt\n'); -// nested/b.js -eval('b content\n\n//# sourceURL=http://concatjs//system_root/bazel-bin/nested/b.js\n'); -` - - if actual != expected { - t.Errorf("Response differs, actual: %s, expected: %s", actual, expected) - } -} - -func runOneRequest(b *testing.B, handler http.Handler, gzip bool) { - req, err := http.NewRequest("GET", "", nil) - if err != nil { - b.Fatal(err) - } - if gzip { - req.Header["Accept-Encoding"] = []string{"gzip"} - } - w := httptest.NewRecorder() - handler.ServeHTTP(w, req) - if w.Code != http.StatusOK { - b.Errorf("HTTP request failed: %d", w.Code) - } -}
diff --git a/packages/concatjs/devserver/concatjs_devserver.bzl b/packages/concatjs/devserver/concatjs_devserver.bzl deleted file mode 100644 index 8f5d1ba..0000000 --- a/packages/concatjs/devserver/concatjs_devserver.bzl +++ /dev/null
@@ -1,256 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"Simple development server" - -load("@build_bazel_rules_nodejs//:providers.bzl", "ExternalNpmPackageInfo", "JSNamedModuleInfo", "node_modules_aspect") -load( - "@build_bazel_rules_nodejs//internal/js_library:js_library.bzl", - "write_amd_names_shim", -) - -# Avoid using non-normalized paths (workspace/../other_workspace/path) -def _to_manifest_path(ctx, file): - if file.short_path.startswith("../"): - return file.short_path[3:] - else: - return ctx.workspace_name + "/" + file.short_path - -def _concatjs_devserver(ctx): - files_depsets = [] - for dep in ctx.attr.deps: - if JSNamedModuleInfo in dep: - files_depsets.append(dep[JSNamedModuleInfo].sources) - if not JSNamedModuleInfo in dep and not ExternalNpmPackageInfo in dep and hasattr(dep, "files"): - # These are javascript files provided by DefaultInfo from a direct - # dep that has no JSNamedModuleInfo provider or ExternalNpmPackageInfo - # provider (not an npm dep). These files must be in named AMD or named - # UMD format. - files_depsets.append(dep.files) - files = depset(transitive = files_depsets) - - # Also include files from npm fine grained deps as inputs. - # These deps are identified by the ExternalNpmPackageInfo provider. - node_modules_depsets = [] - for dep in ctx.attr.deps: - if ExternalNpmPackageInfo in dep: - node_modules_depsets.append(dep[ExternalNpmPackageInfo].sources) - node_modules = depset(transitive = node_modules_depsets) - - workspace_name = ctx.label.workspace_name if ctx.label.workspace_name else ctx.workspace_name - - # Create a manifest file with the sources in arbitrary order, and without - # bazel-bin prefixes ("root-relative paths"). - # TODO(alexeagle): we should experiment with keeping the files toposorted, to - # see if we can get performance gains out of the module loader. - ctx.actions.write(ctx.outputs.manifest, "".join([ - workspace_name + "/" + f.short_path + "\n" - for f in files.to_list() - if f.path.endswith(".js") - ])) - - amd_names_shim = ctx.actions.declare_file( - "_%s.amd_names_shim.js" % ctx.label.name, - sibling = ctx.outputs.script, - ) - write_amd_names_shim(ctx.actions, amd_names_shim, ctx.attr.bootstrap) - - # Requirejs is always needed so its included as the first script - # in script_files before any user specified scripts for the devserver - # to concat in order. - script_files = [] - script_files.extend(ctx.files.bootstrap) - script_files.append(ctx.file._requirejs_script) - script_files.append(amd_names_shim) - script_files.extend(ctx.files.scripts) - ctx.actions.write(ctx.outputs.scripts_manifest, "".join([ - workspace_name + "/" + f.short_path + "\n" - for f in script_files - ])) - - # With cross-platform RBE for OSX & Windows ctx.executable.devserver will be linux as --cpu and - # --host_cpu must be overridden to k8. However, we still want to be able to run the devserver on the host - # machine so we need to include the host devserver binary, which is ctx.executable.devserver_host, in the - # runfiles. For non-RBE and for RBE with a linux host, ctx.executable.devserver & ctx.executable.devserver_host - # will be the same binary. - devserver_runfiles = [ - ctx.executable.devserver, - ctx.executable.devserver_host, - ctx.outputs.manifest, - ctx.outputs.scripts_manifest, - ] - devserver_runfiles += ctx.files.static_files - devserver_runfiles += script_files - devserver_runfiles += ctx.files._bash_runfile_helpers - - packages = depset(["/".join([workspace_name, ctx.label.package])] + ctx.attr.additional_root_paths) - - ctx.actions.expand_template( - template = ctx.file._launcher_template, - output = ctx.outputs.script, - substitutions = { - "TEMPLATED_entry_module": ctx.attr.entry_module, - "TEMPLATED_main": _to_manifest_path(ctx, ctx.executable.devserver), - "TEMPLATED_manifest": _to_manifest_path(ctx, ctx.outputs.manifest), - "TEMPLATED_packages": ",".join(packages.to_list()), - "TEMPLATED_port": str(ctx.attr.port), - "TEMPLATED_scripts_manifest": _to_manifest_path(ctx, ctx.outputs.scripts_manifest), - "TEMPLATED_serving_path": ctx.attr.serving_path if ctx.attr.serving_path else "", - "TEMPLATED_workspace": workspace_name, - }, - is_executable = True, - ) - - return [DefaultInfo( - runfiles = ctx.runfiles( - files = devserver_runfiles, - # We don't expect executable targets to depend on the devserver, but if they do, - # they can see the JavaScript code. - transitive_files = depset(transitive = [files, node_modules]), - collect_data = True, - collect_default = True, - ), - )] - -concatjs_devserver = rule( - implementation = _concatjs_devserver, - attrs = { - "additional_root_paths": attr.string_list( - doc = """Additional root paths to serve `static_files` from. - Paths should include the workspace name such as `["__main__/resources"]` - """, - ), - "bootstrap": attr.label_list( - doc = "Scripts to include in the JS bundle before the module loader (require.js)", - allow_files = [".js"], - ), - "deps": attr.label_list( - doc = "Targets that produce JavaScript, such as `ts_library`", - allow_files = True, - aspects = [node_modules_aspect], - ), - "devserver": attr.label( - doc = """Go based devserver executable. - - With cross-platform RBE for OSX & Windows ctx.executable.devserver will be linux as --cpu and - --host_cpu must be overridden to k8. However, we still want to be able to run the devserver on the host - machine so we need to include the host devserver binary, which is ctx.executable.devserver_host, in the - runfiles. For non-RBE and for RBE with a linux host, ctx.executable.devserver & ctx.executable.devserver_host - will be the same binary. - - Defaults to precompiled go binary setup by @bazel/typescript npm package""", - default = Label("//packages/concatjs/devserver"), - executable = True, - cfg = "exec", - ), - "devserver_host": attr.label( - doc = """Go based devserver executable for the host platform. - Defaults to precompiled go binary setup by @bazel/typescript npm package""", - default = Label("//packages/concatjs/devserver"), - executable = True, - cfg = "exec", - ), - "entry_module": attr.string( - doc = """The `entry_module` should be the AMD module name of the entry module such as `"__main__/src/index".` - `concatjs_devserver` concats the following snippet after the bundle to load the application: - `require(["entry_module"]);` - """, - ), - "port": attr.int( - doc = """The port that the devserver will listen on.""", - default = 5432, - ), - "scripts": attr.label_list( - doc = "User scripts to include in the JS bundle before the application sources", - allow_files = [".js"], - ), - "serving_path": attr.string( - # This default repeats the one in the go program. We make it explicit here so we can read it - # when injecting scripts into the index file. - default = "/_/ts_scripts.js", - doc = """The path you can request from the client HTML which serves the JavaScript bundle. - If you don't specify one, the JavaScript can be loaded at /_/ts_scripts.js""", - ), - "static_files": attr.label_list( - doc = """Arbitrary files which to be served, such as index.html. - They are served relative to the package where this rule is declared.""", - allow_files = True, - ), - "_bash_runfile_helpers": attr.label(default = Label("@build_bazel_rules_nodejs//third_party/github.com/bazelbuild/bazel/tools/bash/runfiles")), - "_launcher_template": attr.label(allow_single_file = True, default = Label("//packages/concatjs/devserver:launcher_template.sh")), - "_requirejs_script": attr.label(allow_single_file = True, default = Label("//packages/concatjs/third_party/npm/requirejs:require.js")), - }, - outputs = { - "manifest": "%{name}.MF", - "script": "%{name}.sh", - "scripts_manifest": "scripts_%{name}.MF", - }, - doc = """concatjs_devserver is a simple development server intended for a quick "getting started" experience. - -Additional documentation [here](https://github.com/alexeagle/angular-bazel-example/wiki/Running-a-devserver-under-Bazel) -""", -) - -def concatjs_devserver_macro(name, args = [], visibility = None, tags = [], testonly = 0, **kwargs): - """Macro for creating a `concatjs_devserver` - - This macro re-exposes a `sh_binary` and `concatjs_devserver` target that can run the - actual devserver implementation. - The `concatjs_devserver` rule is just responsible for generating a launcher script - that runs the Go devserver implementation. The `sh_binary` is the primary - target that matches the specified "name" and executes the generated bash - launcher script. - This is re-exported in `//:index.bzl` as `concatjs_devserver` so if you load the rule - from there, you actually get this macro. - - Args: - name: Name of the devserver target - args: Command line arguments that will be passed to the devserver Go implementation - visibility: Visibility of the devserver targets - tags: Standard Bazel tags, this macro adds a couple for ibazel - testonly: Whether the devserver should only run in `bazel test` - **kwargs: passed through to `concatjs_devserver` - """ - concatjs_devserver( - name = "%s_launcher" % name, - testonly = testonly, - visibility = ["//visibility:private"], - tags = tags, - **kwargs - ) - - # Expose the manifest file label - native.alias( - name = "%s.MF" % name, - actual = "%s_launcher.MF" % name, - tags = tags, - visibility = visibility, - ) - - native.sh_binary( - name = name, - args = args, - # Users don't need to know that these tags are required to run under ibazel - tags = tags + [ - # Tell ibazel not to restart the devserver when its deps change. - "ibazel_notify_changes", - # Tell ibazel to serve the live reload script, since we expect a browser will connect to - # this program. - "ibazel_live_reload", - ], - srcs = ["%s_launcher.sh" % name], - data = [":%s_launcher" % name], - testonly = testonly, - visibility = visibility, - )
diff --git a/packages/concatjs/devserver/devserver/BUILD.bazel b/packages/concatjs/devserver/devserver/BUILD.bazel deleted file mode 100644 index e2fc700..0000000 --- a/packages/concatjs/devserver/devserver/BUILD.bazel +++ /dev/null
@@ -1,25 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "go_default_library", - srcs = ["devserver.go"], - importpath = "github.com/bazelbuild/rules_typescript/devserver/devserver", - visibility = ["//visibility:public"], - deps = [ - "//packages/concatjs/devserver/runfiles:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["devserver_test.go"], - # Required runfiles for the devserver tests. - data = [ - "test/index.html", - "test/relative.html", - "@devserver_test_workspace//:sources", - ], - embed = [":go_default_library"], - # FIXME: broken on Windows - tags = ["fix-windows"], -)
diff --git a/packages/concatjs/devserver/devserver/devserver.go b/packages/concatjs/devserver/devserver/devserver.go deleted file mode 100644 index 3ed5d88..0000000 --- a/packages/concatjs/devserver/devserver/devserver.go +++ /dev/null
@@ -1,223 +0,0 @@ -// Package devserver provides code shared between Bazel and Blaze. -package devserver - -import ( - "bytes" - "fmt" - "net" - "net/http" - "net/url" - "os" - "path/filepath" - "strings" - "time" - - "github.com/bazelbuild/rules_typescript/devserver/runfiles" -) - -// Convert Windows paths separators. -var pathReplacer = strings.NewReplacer("\\", "/") - -func shouldAllowCors(request *http.Request) bool { - hostname, err := os.Hostname() - if err != nil { - return false - } - referer, err := url.Parse(request.Header.Get("Origin")) - if err != nil { - return false - } - host, _, err := net.SplitHostPort(referer.Host) - // SplitHostPort fails when the parameter doesn't have a port. - if err != nil { - host = referer.Host - } - return host == hostname || host == "localhost" -} - -func customNotFoundMiddleware(notFound http.HandlerFunc, passThrough http.HandlerFunc) http.HandlerFunc { - return func(writer http.ResponseWriter, request *http.Request) { - passThrough( - &customNotFoundResponseWriter{ResponseWriter: writer, request: request, notFound: notFound}, - request, - ) - } -} - -type customNotFoundResponseWriter struct { - http.ResponseWriter - - request *http.Request - notFound http.HandlerFunc - has404 bool - hasWrite bool -} - -// Write implements http.ResponseWriter.Write. -func (w *customNotFoundResponseWriter) Write(b []byte) (int, error) { - w.hasWrite = true - if w.has404 { - // We have already written the not found response, so drop this one. - return len(b), nil - } - return w.ResponseWriter.Write(b) -} - -// WriteHeader implements http.ResponseWriter.WriteHeader. -func (w *customNotFoundResponseWriter) WriteHeader(code int) { - if code != http.StatusNotFound || w.hasWrite { - // We only intercept not found statuses. We also don't intercept statuses written after the - // first write as these are an error and should be handled by the default ResponseWriter. - w.ResponseWriter.WriteHeader(code) - return - } - - // WriteHeader writes out the entire header (including content type) and only the first call - // will succeed. Therefore, if we want the correct content type set, we must set it here. - w.Header().Del("Content-Type") - w.Header().Add("Content-Type", "text/html; charset=utf-8") - w.ResponseWriter.WriteHeader(code) - w.has404 = true - - // We have already written the header, so drop any calls to WriteHeader made by the not found - // handler. These additional calls are expected, and if passed through, would cause the base - // ResponseWriter to unnecessarily spam the error log. - w.notFound(&headerSuppressorResponseWriter{w.ResponseWriter}, w.request) - w.hasWrite = true -} - -type headerSuppressorResponseWriter struct { - http.ResponseWriter -} - -// WriteHeader implements http.ResponseWriter.WriteHeader. -func (w *headerSuppressorResponseWriter) WriteHeader(code int) {} - -// CreateFileHandler returns an http handler to locate files on disk -func CreateFileHandler(servingPath, manifest string, pkgs []string, base string) http.HandlerFunc { - // We want to add the root runfile path because by default developers should be able to request - // runfiles through their absolute manifest path (e.g. "my_workspace_name/src/file.css") - pkgPaths := dirHTTPFileSystem{append(pkgs, "./"), base} - - fileHandler := http.FileServer(pkgPaths).ServeHTTP - - defaultPage := []byte(fmt.Sprintf(`<!doctype html> - <html> - <head> - <title>ts_devserver (%s)</title> - </head> - <body> - <script src="%s"></script> - </body> - </html> - `, manifest, servingPath)) - - // indexHandler serves an index.html if present, or otherwise serves a minimal - // generated index.html with a script tag to include the bundled js source. - indexHandler := func(w http.ResponseWriter, r *http.Request) { - // search through pkgs for the first index.html file found if any exists - for _, pkg := range pkgs { - // File path is not cached, so that a user's edits will be reflected. - userIndexFile, err := runfiles.Runfile(base, pathReplacer.Replace(filepath.Join(pkg, "index.html"))) - - // In case the potential user index file couldn't be found in the runfiles, - // continue searching within other packages. - if err != nil { - continue - } - - // We can assume that the file is readable if it's listed in the runfiles manifest. - http.ServeFile(w, r, userIndexFile) - return - } - content := bytes.NewReader(defaultPage) - http.ServeContent(w, r, "index.html", time.Now(), content) - } - - // Serve a custom index.html so as to override the default directory listing - // from http.FileServer when no index.html file present. - indexOnNotFoundHandler := func(writer http.ResponseWriter, request *http.Request) { - // The browser can't tell the difference between different source checkouts or different devserver - // instances, so it may mistakenly cache static files (including templates) using versions from - // old instances if they haven't been modified more recently. To prevent this, we force no-cache - // on all static files. - writer.Header().Add("Cache-Control", "no-cache, no-store, must-revalidate") - if shouldAllowCors(request) { - writer.Header().Add("Access-Control-Allow-Origin", request.Header.Get("Origin")) - writer.Header().Add("Access-Control-Allow-Credentials", "true") - } - writer.Header().Add("Pragma", "no-cache") - writer.Header().Add("Expires", "0") - // Add gzip headers if serving .gz files. - if strings.HasSuffix(request.URL.EscapedPath(), ".gz") { - writer.Header().Add("Content-Encoding", "gzip") - } - - if request.URL.Path == "/" { - indexHandler(writer, request) - return - } - // When a file is not found, serve a 404 code but serve the index.html from above as its body. - // This allows applications to use html5 routing and reload the page at /some/sub/path, but still - // get their web app served. - // The responses is marked as an error (404) so that requests that are genuinely wrong (e.g. - // incorrect URLs for CSS, images, etc) are marked as such. Otherwise they'd seem to succeed but - // then fail to process correctly, which makes for a bad debugging experience. - writer = &customNotFoundResponseWriter{ResponseWriter: writer, request: request, notFound: indexHandler} - fileHandler(writer, request) - } - - return indexOnNotFoundHandler -} - -// dirHTTPFileSystem implements http.FileSystem by looking in the list of dirs one after each other. -type dirHTTPFileSystem struct { - packageDirs []string - base string -} - -func (fs dirHTTPFileSystem) Open(name string) (http.File, error) { - for _, packageName := range fs.packageDirs { - manifestFilePath := filepath.Join(packageName, name) - realFilePath, err := runfiles.Runfile(fs.base, manifestFilePath) - - if err != nil { - // In case the runfile could not be found, we also need to check that the requested - // path does not refer to a directory containing an "index.html" file. This can - // happen if Bazel runs without runfile symlinks, where only files can be resolved - // from the manifest. In that case we dirty check if there is a "index.html" file. - realFilePath, err = runfiles.Runfile(fs.base, filepath.Join(manifestFilePath, "index.html")) - - // Continue searching if the runfile couldn't be found for the requested file. - if err != nil { - continue - } - } - - stat, err := os.Stat(realFilePath) - if err != nil { - // This should actually never happen because runfiles resolved through the runfile helpers - // should always exist. Just in order to properly handle the error, we add this error handling. - return nil, fmt.Errorf("could not read runfile %s", manifestFilePath) - } - - // In case the resolved file resolves to a directory. This can only happen if - // Bazel runs with symlinked runfiles (e.g. on MacOS, linux). In that case, we - // just look for a index.html in the directory. - if stat.IsDir() { - realFilePath, err = runfiles.Runfile(fs.base, filepath.Join(manifestFilePath, "index.html")) - - // In case the index.html file of the requested directory couldn't be found, - // we just continue searching. - if err != nil { - continue - } - } - - // We can assume that the file is present, if it's listed in the runfile manifest. Though, we - // return the error, in case something prevented the read-access. - return os.Open(realFilePath) - } - - return nil, os.ErrNotExist -}
diff --git a/packages/concatjs/devserver/devserver/devserver_test.go b/packages/concatjs/devserver/devserver/devserver_test.go deleted file mode 100644 index 8b0b2b7..0000000 --- a/packages/concatjs/devserver/devserver/devserver_test.go +++ /dev/null
@@ -1,157 +0,0 @@ -package devserver - -import ( - "fmt" - "io/ioutil" - "net/http" - "net/http/httptest" - "os" - "path/filepath" - "strings" - "testing" -) - -func tmpfile(t *testing.T, name, contents string) (string, func()) { - fullPath := filepath.Join(os.Getenv("TEST_TMPDIR"), name) - dir := filepath.Dir(fullPath) - if _, err := os.Stat(dir); err != nil { - if os.IsNotExist(err) { - if err := os.MkdirAll(dir, 0777); err != nil { - t.Fatalf("failed to create dir: %v", err) - } - } else { - t.Fatalf("failed to stat dir: %v", err) - } - } - err := ioutil.WriteFile(fullPath, []byte(contents), 0666) - if err != nil { - t.Fatalf("failed to write temp file: %v", err) - } - return fullPath, func() { - if err := os.Remove(fullPath); err != nil && !os.IsNotExist(err) { - t.Errorf("failed to delete file %q: %v", fullPath, err) - } - } -} - -func req(handler http.HandlerFunc, url string) (int, string) { - req := httptest.NewRequest("GET", url, nil) - w := httptest.NewRecorder() - handler(w, req) - resp := w.Result() - body, _ := ioutil.ReadAll(resp.Body) - return resp.StatusCode, string(body) -} - -func TestDevserverFileHandling(t *testing.T) { - handler := CreateFileHandler("/app.js", "manifest.MF", []string{ - // This verifies that we can resolve relatively to the current package. Usually the - // devserver Bazel rule adds the current package here. - "build_bazel_rules_nodejs/packages/concatjs/devserver/devserver", - // Verifies that we can specify subfolders of workspaces - "build_bazel_rules_nodejs/packages/concatjs/devserver/devserver/test", - // Verifies that we can specify external workspaces as root dirs. - "devserver_test_workspace", - // Verifies that we can specify subfolders from external workspaces. - "devserver_test_workspace/pkg2", - }, "") - - defaultPageContent := `<script src="/app.js">` - - tests := []struct { - code int - url string - content string - }{ - // index file from pkg1. - {http.StatusOK, "/", "contents of index.html"}, - // index file as a response to not found handler. - {http.StatusNotFound, "/no/such/dir", "contents of index.html"}, - // index file as a response to not found handler. - {http.StatusNotFound, "/no/such/dir/", "contents of index.html"}, - // index file as a response to a directory that is found. - {http.StatusNotFound, "/pkg2/", "contents of index.html"}, - // file from relative to base package. - {http.StatusOK, "/test/relative.html", "contents of relative.html"}, - // file from the base package with full path. - {http.StatusOK, "/pkg1/foo.html", "contents of foo.html"}, - // file from pkg2. - {http.StatusOK, "/bar.html", "contents of bar.html"}, - // file from pkg2 with full path. - {http.StatusOK, "/pkg2/bar.html", "contents of bar.html"}, - // index file from disk - {http.StatusOK, "/rpc/items", "contents of rpc/items/index.html"}, - // file from an unrelated package. - {http.StatusOK, "/pkg3/baz.html", "contents of baz.html in pkg3"}, - } - - for _, tst := range tests { - code, body := req(handler, fmt.Sprintf("http://test%s", tst.url)) - if code != tst.code { - t.Errorf("got %d, expected %d", code, tst.code) - } - if !strings.Contains(body, tst.content) { - t.Errorf("expected %q to contain %q, got %q", tst.url, tst.content, body) - } - if strings.Contains(body, defaultPageContent) { - t.Errorf("got %q, default page shouldn't be part of response", body) - } - } -} - -func TestDevserverGeneratedIndexFile(t *testing.T) { - handler := CreateFileHandler("/app.js", "manifest.MF", []string{ - "devserver_test_workspace", - }, "") - defaultPageContent := `<script src="/app.js">` - - tests := []struct { - code int - url string - content string - }{ - // Assert generated index for root. - {http.StatusOK, "/", defaultPageContent}, - // Assert generated index as a response to not found handler. - {http.StatusNotFound, "/no/such/dir", defaultPageContent}, - // Assert index file as a response to a directory that is found, but does not - // have an index file. - {http.StatusNotFound, "/pkg2/", defaultPageContent}, - } - - for _, tst := range tests { - code, body := req(handler, fmt.Sprintf("http://test%s", tst.url)) - if code != tst.code { - t.Errorf("got %d, expected %d", code, tst.code) - } - if !strings.Contains(body, tst.content) { - t.Errorf("expected %q to contain %q, got %q", tst.url, tst.content, body) - } - } -} - -func TestDevserverAbsoluteRunfileRequest(t *testing.T) { - handler := CreateFileHandler("/app.js", "manifest.MF", []string{}, "") - - tests := []struct { - code int - url string - content string - }{ - // Assert that it's possible to request a runfile through it's absolute manifest path. - {http.StatusOK, "/devserver_test_workspace/pkg2/bar.html", "contents of bar.html"}, - // Assert that it's possible to request a runfile directory through it's absolute manifest path. This - // should resolve to the directories "index.html" file. - {http.StatusOK, "/devserver_test_workspace/pkg1", "contents of index.html"}, - } - - for _, tst := range tests { - code, body := req(handler, fmt.Sprintf("http://test%s", tst.url)) - if code != tst.code { - t.Errorf("got %d, expected %d", code, tst.code) - } - if !strings.Contains(body, tst.content) { - t.Errorf("expected %q to contain %q, got %q", tst.url, tst.content, body) - } - } -}
diff --git a/packages/concatjs/devserver/devserver/test/index.html b/packages/concatjs/devserver/devserver/test/index.html deleted file mode 100644 index c04b444..0000000 --- a/packages/concatjs/devserver/devserver/test/index.html +++ /dev/null
@@ -1 +0,0 @@ -contents of index.html \ No newline at end of file
diff --git a/packages/concatjs/devserver/devserver/test/relative.html b/packages/concatjs/devserver/devserver/test/relative.html deleted file mode 100644 index a0e4874..0000000 --- a/packages/concatjs/devserver/devserver/test/relative.html +++ /dev/null
@@ -1 +0,0 @@ -contents of relative.html \ No newline at end of file
diff --git a/packages/concatjs/devserver/devserver/test/test-workspace/BUILD.bazel b/packages/concatjs/devserver/devserver/test/test-workspace/BUILD.bazel deleted file mode 100644 index 85d4777..0000000 --- a/packages/concatjs/devserver/devserver/test/test-workspace/BUILD.bazel +++ /dev/null
@@ -1,5 +0,0 @@ -filegroup( - name = "sources", - srcs = glob(["**/*"]), - visibility = ["//visibility:public"], -)
diff --git a/packages/concatjs/devserver/devserver/test/test-workspace/WORKSPACE b/packages/concatjs/devserver/devserver/test/test-workspace/WORKSPACE deleted file mode 100644 index e69de29..0000000 --- a/packages/concatjs/devserver/devserver/test/test-workspace/WORKSPACE +++ /dev/null
diff --git a/packages/concatjs/devserver/devserver/test/test-workspace/pkg1/foo.html b/packages/concatjs/devserver/devserver/test/test-workspace/pkg1/foo.html deleted file mode 100644 index 7923b1b..0000000 --- a/packages/concatjs/devserver/devserver/test/test-workspace/pkg1/foo.html +++ /dev/null
@@ -1 +0,0 @@ -contents of foo.html \ No newline at end of file
diff --git a/packages/concatjs/devserver/devserver/test/test-workspace/pkg1/index.html b/packages/concatjs/devserver/devserver/test/test-workspace/pkg1/index.html deleted file mode 100644 index c04b444..0000000 --- a/packages/concatjs/devserver/devserver/test/test-workspace/pkg1/index.html +++ /dev/null
@@ -1 +0,0 @@ -contents of index.html \ No newline at end of file
diff --git a/packages/concatjs/devserver/devserver/test/test-workspace/pkg2/bar.html b/packages/concatjs/devserver/devserver/test/test-workspace/pkg2/bar.html deleted file mode 100644 index 70646c6..0000000 --- a/packages/concatjs/devserver/devserver/test/test-workspace/pkg2/bar.html +++ /dev/null
@@ -1 +0,0 @@ -contents of bar.html \ No newline at end of file
diff --git a/packages/concatjs/devserver/devserver/test/test-workspace/pkg2/foo.html b/packages/concatjs/devserver/devserver/test/test-workspace/pkg2/foo.html deleted file mode 100644 index 9c5a98c..0000000 --- a/packages/concatjs/devserver/devserver/test/test-workspace/pkg2/foo.html +++ /dev/null
@@ -1 +0,0 @@ -contents of foo.html in pkg2 \ No newline at end of file
diff --git a/packages/concatjs/devserver/devserver/test/test-workspace/pkg2/rpc/items/index.html b/packages/concatjs/devserver/devserver/test/test-workspace/pkg2/rpc/items/index.html deleted file mode 100644 index 3850cc9..0000000 --- a/packages/concatjs/devserver/devserver/test/test-workspace/pkg2/rpc/items/index.html +++ /dev/null
@@ -1 +0,0 @@ -contents of rpc/items/index.html \ No newline at end of file
diff --git a/packages/concatjs/devserver/devserver/test/test-workspace/pkg3/baz.html b/packages/concatjs/devserver/devserver/test/test-workspace/pkg3/baz.html deleted file mode 100644 index f471ef4..0000000 --- a/packages/concatjs/devserver/devserver/test/test-workspace/pkg3/baz.html +++ /dev/null
@@ -1 +0,0 @@ -contents of baz.html in pkg3 \ No newline at end of file
diff --git a/packages/concatjs/devserver/launcher_template.sh b/packages/concatjs/devserver/launcher_template.sh deleted file mode 100644 index b9d5e85..0000000 --- a/packages/concatjs/devserver/launcher_template.sh +++ /dev/null
@@ -1,80 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2018 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# --- begin runfiles.bash initialization v2 --- -# Copy-pasted from the Bazel Bash runfiles library v2. -set -uo pipefail; f=build_bazel_rules_nodejs/third_party/github.com/bazelbuild/bazel/tools/bash/runfiles/runfiles.bash -source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \ - source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \ - source "$0.runfiles/$f" 2>/dev/null || \ - source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ - source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ - { echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e -# --- end runfiles.bash initialization v2 --- - -# Check environment for which node path to use -unameOut="$(uname -s)" -case "${unameOut}" in - Linux*) machine=linux ;; - Darwin*) machine=darwin ;; - CYGWIN*) machine=windows ;; - MINGW*) machine=windows ;; - MSYS_NT*) machine=windows ;; - *) machine=linux - printf "\nUnrecongized uname '${unameOut}'; defaulting to use node for linux.\n" >&2 - printf "Please file an issue to https://github.com/bazelbuild/rules_nodejs/issues if \n" >&2 - printf "you would like to add your platform to the supported concatjs_devserver platforms.\n\n" >&2 - ;; -esac - -case "${machine}" in - # The following paths must match up with //packages/concatjs/devserver binaries - # FIXME: we shouldn't hardcode "npm" repository name here - darwin) readonly platform_main_manifest="npm/@bazel/concatjs/devserver/devserver-darwin_x64" ;; - windows) readonly platform_main_manifest="npm/@bazel/concatjs/devserver/devserver-windows_x64.exe" ;; - *) readonly platform_main_manifest="npm/@bazel/concatjs/devserver/devserver-linux_x64" ;; -esac - -readonly platform_main=$(rlocation "${platform_main_manifest}") - -if [ -f "${platform_main}" ]; then - readonly main=${platform_main} -else - # If the devserver binary is overridden then use the templated binary - readonly main=$(rlocation "TEMPLATED_main") -fi - -if [ ! -f "${main}" ]; then - printf "\n>>>> FAIL: The concatjs_devserver binary '${main_platform}' not found in runfiles.\n" >&2 - printf "This node toolchain was chosen based on your uname '${unameOut}'.\n" >&2 - printf "Please file an issue to https://github.com/bazelbuild/rules_nodejs/issues if \n" >&2 - printf "you would like to add your platform to the supported concatjs_devserver platforms. <<<<\n\n" >&2 - exit 1 -fi - -readonly manifest=$(rlocation "TEMPLATED_manifest") -readonly scripts_manifest=$(rlocation "TEMPLATED_scripts_manifest") - -# Workaround for https://github.com/bazelbuild/bazel/issues/6764 -# If this issue is incorporated into Bazel, the workaround here should be removed. -MSYS2_ARG_CONV_EXCL="*" "${main}" \ - -packages=TEMPLATED_packages \ - -serving_path=TEMPLATED_serving_path \ - -entry_module=TEMPLATED_entry_module \ - -port=TEMPLATED_port \ - -manifest="${manifest}" \ - -scripts_manifest="${scripts_manifest}" \ - "$@"
diff --git a/packages/concatjs/devserver/main.go b/packages/concatjs/devserver/main.go deleted file mode 100644 index 466a63d..0000000 --- a/packages/concatjs/devserver/main.go +++ /dev/null
@@ -1,162 +0,0 @@ -package main - -import ( - "bufio" - "flag" - "fmt" - "io" - "io/ioutil" - "net/http" - "os" - "strings" - - "github.com/bazelbuild/rules_typescript/devserver/concatjs" - "github.com/bazelbuild/rules_typescript/devserver/devserver" - "github.com/bazelbuild/rules_typescript/devserver/runfiles" -) - -var ( - port = flag.Int("port", 5432, "server port to listen on") - // The "base" CLI flag is only kept because within Google3 because removing would be a breaking change due to - // ConcatJS and "devserver/devserver.go" still respecting the specified base flag. - base = flag.String("base", "", "server base (required, runfiles of the binary)") - pkgs = flag.String("packages", "", "root package(s) to serve, comma-separated") - manifest = flag.String("manifest", "", "sources manifest (.MF)") - scriptsManifest = flag.String("scripts_manifest", "", "preScripts manifest (.MF)") - servingPath = flag.String("serving_path", "/_/ts_scripts.js", "path to serve the combined sources at") - entryModule = flag.String("entry_module", "", "entry module name") -) - -func main() { - flag.Parse() - - if len(*pkgs) == 0 || (*manifest == "") || (*scriptsManifest == "") { - fmt.Fprintf(os.Stderr, "Required argument not set\n") - os.Exit(1) - } - - manifestPath, err := runfiles.Runfile(*base, *scriptsManifest) - - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to find scripts_manifest in runfiles: %v\n", err) - os.Exit(1) - } - - scriptFiles, err := manifestFiles(manifestPath) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to read scripts_manifest: %v\n", err) - os.Exit(1) - } - - if !strings.HasPrefix(*servingPath, "/") { - fmt.Fprintf(os.Stderr, "The specified serving_path does not start with a slash. "+ - "This causes the serving path to not have any effect.\n") - os.Exit(1) - } - - preScripts := make([]string, 0, 100) - postScripts := make([]string, 0, 1) - - // Include the livereload script if IBAZEL_LIVERELOAD_URL is set. - livereloadUrl := os.Getenv("IBAZEL_LIVERELOAD_URL") - if livereloadUrl != "" { - fmt.Printf("Serving livereload script from %s\n", livereloadUrl) - livereloadLoaderSnippet := fmt.Sprintf(`(function(){ - const script = document.createElement('script'); - script.src = "%s"; - document.head.appendChild(script); -})();`, livereloadUrl) - preScripts = append(preScripts, livereloadLoaderSnippet) - } - - // Include the profiler script if IBAZEL_PROFILER_URL is set. - profilerScriptURL := os.Getenv("IBAZEL_PROFILER_URL") - if profilerScriptURL != "" { - fmt.Printf("Serving profiler script from %s\n", profilerScriptURL) - profilerLoaderSnippet := fmt.Sprintf(`(function(){ - const script = document.createElement('script'); - script.src = "%s"; - document.head.appendChild(script); -})();`, profilerScriptURL) - preScripts = append(preScripts, profilerLoaderSnippet) - } - - // Include all user scripts in preScripts. This should always include - // the requirejs script which is added to scriptFiles by the devserver - // skylark rule. - for _, v := range scriptFiles { - runfile, err := runfiles.Runfile(*base, v) - if err != nil { - fmt.Fprintf(os.Stderr, "Could not find runfile %s, got error %s", v, err) - } - - js, err := loadScript(runfile) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to read script %s: %v\n", v, err) - } else { - preScripts = append(preScripts, js) - } - } - - // If the entryModule is set then add a snippet to load - // the application to postScripts to be outputted after the sources - if *entryModule != "" { - postScripts = append(postScripts, fmt.Sprintf("require([\"%s\"]);", *entryModule)) - } - - http.Handle(*servingPath, concatjs.ServeConcatenatedJS(*manifest, *base, preScripts, postScripts, - &RunfileFileSystem{})) - pkgList := strings.Split(*pkgs, ",") - http.HandleFunc("/", devserver.CreateFileHandler(*servingPath, *manifest, pkgList, *base)) - - h, err := os.Hostname() - if err != nil { - h = "localhost" - } - // Detect if we are running in a linux container inside ChromeOS - // If so, we assume you want to use the native browser (outside the container) - // so you'll need to modify the hostname to access the server - if _, err := os.Stat("/etc/apt/sources.list.d/cros.list"); err == nil { - h = h + ".linux.test" - } - - fmt.Printf("Server listening on http://%s:%d/\n", h, *port) - fmt.Fprintln(os.Stderr, http.ListenAndServe(fmt.Sprintf(":%d", *port), nil).Error()) - os.Exit(1) -} - -func loadScript(path string) (string, error) { - buf, err := ioutil.ReadFile(path) - if err != nil { - return "", err - } - return fmt.Sprintf("// %s\n%s", path, buf), nil -} - -// manifestFiles parses a manifest, returning a list of the files in the manifest. -func manifestFiles(manifest string) ([]string, error) { - f, err := os.Open(manifest) - if err != nil { - return nil, fmt.Errorf("could not read manifest %s: %s", manifest, err) - } - defer f.Close() - return manifestFilesFromReader(f) -} - -// manifestFilesFromReader is a helper for manifestFiles, split out for testing. -func manifestFilesFromReader(r io.Reader) ([]string, error) { - var lines []string - s := bufio.NewScanner(r) - for s.Scan() { - path := s.Text() - if path == "" { - continue - } - lines = append(lines, path) - } - if err := s.Err(); err != nil { - return nil, err - } - - return lines, nil -}
diff --git a/packages/concatjs/devserver/runfile-filesystem.go b/packages/concatjs/devserver/runfile-filesystem.go deleted file mode 100644 index 090ded6..0000000 --- a/packages/concatjs/devserver/runfile-filesystem.go +++ /dev/null
@@ -1,35 +0,0 @@ -// Main package that provides a command line interface for starting a Bazel devserver -// using Bazel runfile resolution and ConcatJS for in-memory bundling of specified AMD files. -package main - -import ( - "io/ioutil" - "os" - "time" - - "github.com/bazelbuild/rules_typescript/devserver/runfiles" -) - -// RunfileFileSystem implements FileSystem type from concatjs. -type RunfileFileSystem struct{} - -// StatMtime gets the filestamp for the last file modification. -func (fs *RunfileFileSystem) StatMtime(filename string) (time.Time, error) { - s, err := os.Stat(filename) - if err != nil { - return time.Time{}, err - } - return s.ModTime(), nil -} - -// ReadFile reads a file given its file name -func (fs *RunfileFileSystem) ReadFile(filename string) ([]byte, error) { - return ioutil.ReadFile(filename) -} - -// ResolvePath resolves the specified path within a given root using Bazel's runfile resolution. -// This is necessary because on Windows, runfiles are not symlinked and need to be -// resolved using the runfile manifest file. -func (fs *RunfileFileSystem) ResolvePath(root string, manifestFilePath string) (string, error) { - return runfiles.Runfile(root, manifestFilePath) -}
diff --git a/packages/concatjs/devserver/runfiles/BUILD.bazel b/packages/concatjs/devserver/runfiles/BUILD.bazel deleted file mode 100644 index 2285f5b..0000000 --- a/packages/concatjs/devserver/runfiles/BUILD.bazel +++ /dev/null
@@ -1,17 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -# Gazelle by default tries to map the import for the Bazel runfile go library to a repository with -# an auto-generated name. This does not work for us because the rules_go repository name is different. -# This gazelle directive ensures that Gazelle resolves the import to the proper Bazel label. -# Read more here: https://github.com/bazelbuild/bazel-gazelle#directives -# gazelle:resolve go github.com/bazelbuild/rules_go/go/tools/bazel @io_bazel_rules_go//go/tools/bazel:go_default_library - -go_library( - name = "go_default_library", - srcs = ["runfiles.go"], - importpath = "github.com/bazelbuild/rules_typescript/devserver/runfiles", - visibility = ["//visibility:public"], - deps = [ - "@io_bazel_rules_go//go/tools/bazel:go_default_library", - ], -)
diff --git a/packages/concatjs/devserver/runfiles/runfiles.go b/packages/concatjs/devserver/runfiles/runfiles.go deleted file mode 100644 index 3db23d9..0000000 --- a/packages/concatjs/devserver/runfiles/runfiles.go +++ /dev/null
@@ -1,29 +0,0 @@ -// Package runfiles that provides utility helpers for resolving Bazel runfiles within Go. -package runfiles - -import ( - "path/filepath" - "strings" - - "github.com/bazelbuild/rules_go/go/tools/bazel" -) - -// Runfile returns the base directory to the bazel runfiles -func Runfile(_base string, manifestPath string) (string, error) { - // Absolute paths don't require a lookup in the runfiles manifest - if filepath.IsAbs(manifestPath) { - return manifestPath, nil - } - // Very hacky workaround for breaking change in rules_go - // https://github.com/bazelbuild/rules_go/pull/2076#issuecomment-520628083 - // devserver was originally written to resolve full paths from the runfiles manifest - // including the workspace segment, but rules_go no longer includes the workspace - // segment in the lookup. We simply remove the first path segment before calling - // through to the rules_go runfiles helper library. - parts := strings.SplitN(manifestPath, "/", 2) - if len(parts) < 2 { - return bazel.Runfile(manifestPath) - } - // throw away parts[0] which is the name of the repository - return bazel.Runfile(parts[1]) -}
diff --git a/packages/concatjs/index.bzl b/packages/concatjs/index.bzl deleted file mode 100644 index 678b9eb..0000000 --- a/packages/concatjs/index.bzl +++ /dev/null
@@ -1,33 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Public API surface is re-exported here. - -Users should not load files under "/internal" -""" - -load("//packages/concatjs/devserver:concatjs_devserver.bzl", _concatjs_devserver = "concatjs_devserver_macro") -load( - "//packages/concatjs/web_test:karma_web_test.bzl", - _karma_web_test = "karma_web_test", - _karma_web_test_suite = "karma_web_test_suite", -) -load("//packages/concatjs/internal:build_defs.bzl", _ts_library = "ts_library_macro") -load("//packages/concatjs/internal:ts_config.bzl", _ts_config = "ts_config") - -karma_web_test = _karma_web_test -karma_web_test_suite = _karma_web_test_suite -concatjs_devserver = _concatjs_devserver -ts_library = _ts_library -ts_config = _ts_config
diff --git a/packages/concatjs/index.docs.bzl b/packages/concatjs/index.docs.bzl deleted file mode 100644 index 88616f6..0000000 --- a/packages/concatjs/index.docs.bzl +++ /dev/null
@@ -1,198 +0,0 @@ -# Copyright 2019 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This contains references to the symbols we want documented. -# We can't point stardoc to the top-level index.bzl since then it will see macros rather than the rules they wrap. -# So this is a copy of index.bzl with macro indirection removed. -""" -# @bazel/concatjs - -Concatjs is a JavaScript bundler, in a trivial sense: the UNIX `cat` command is a basic implementation: - -```bash -$ cat one.js two.js > bundle.js -``` - -Clearly self-evident is that this bundler is super-fast and simple. -A performant implementation adds some in-memory caching, and for developer ergonomics you add a simple IIFE wrapper -around each file so that the Chrome DevTools shows the files in the tree as if they had been independently loaded. - -However at its core, concatjs requires a big tradeoff of a migration cost to buy-in, to get this incredible performance. -The path of the JavaScript files is lost in the bundling process, so they must contain their module ID internally. - -[Named AMD/UMD modules](https://requirejs.org/docs/whyamd.html#namedmodules) and `goog.module` are the two JS module formats that are compatible with concatjs. -Most packages do not ship with this format, so in order to use concatjs tooling, you have to shim your code and dependencies. See the [Compatibility](#compatibility) section below. - -This is at the core of how Google does JavaScript development. -So Bazel rules that originated in Google's codebase have affordances for concatjs. -For example `ts_library` produces named AMD modules in its "devmode" output, and -`karma_web_test` expects to bundle inputs using concatjs. - -## Compatibility - -### First-party code - -First-party code has to be authored as named AMD/UMD modules. -This is also historically referred to as "RequireJS" modules since that's the -JS loader that is typically used with them. - -If you write TypeScript, you can do this following their [documentation](https://www.typescriptlang.org/docs/handbook/modules.html). - -There is an example in this repository: we have an `index.ts` file that wants -to be used with require.js `require("@bazel/concatjs")`. -So it -[declares -that module name](https://github.com/bazelbuild/rules_nodejs/blob/bd53eb524ea3bd56b46b7a5f2eff700443e281ec/packages/concatjs/index.ts#L1) -using the TS triple-slash syntax: - -```typescript -///<amd-module name="@bazel/concatjs"/> -``` - -it is [also compiled with](https://github.com/bazelbuild/rules_nodejs/blob/bd53eb524ea3bd56b46b7a5f2eff700443e281ec/packages/concatjs/BUILD.bazel#L28) -the `"compilerOptions": { "module": "umd" }` TypeScript setting. - -### Third-party code - -To make it easier to produce a UMD version of a third-party npm package, we automatically generate a target that uses Browserify to build one, using the `main` entry from the package's `package.json`. -In most cases this will make the package loadable under concatjs. -This target has a `__umd` suffix. For example, if your library is at `@npm//foo` then the UMD target is `@npm//foo:foo__umd`. - -An example where this fixes a users issue: <https://github.com/bazelbuild/rules_nodejs/issues/2317#issuecomment-735921318> - -In some cases, the generated UMD bundle is not sufficient, and in others it fails to build because it requires some special Browserify configuration. -You can always write your own shim that grabs a symbol from a package you use, and exposes it in an AMD/require.js-compatible way. -For example, even though RxJS ships with a UMD bundle, it contains multiple entry points and uses anonymous modules, not named modules. So our Angular/concatjs example has a `rxjs_shims.js` file that exposes some RxJS operators, then at <https://github.com/bazelbuild/rules_nodejs/blob/2.3.1/examples/angular/src/BUILD.bazel#L65-L71> this is combined in a `filegroup` with the `rxjs.umd.js` file. Now we use this filegroup target when depending on RxJS in a `concatjs_*` rule. - -Ultimately by using concatjs, you're signing up for at least a superficial understanding of these shims and may need to update them when you change your dependencies. - -## Serving JS in development mode under Bazel - -There are two choices for development mode: - -1. Use the `concatjs_devserver` rule to bring up our simple, fast development server. - This is intentionally very simple, to help you get started quickly. However, - since there are many development servers available, we do not want to mirror - their features in yet another server we maintain. -2. Teach your real frontend server to serve files from Bazel's output directory. - This is not yet documented. Choose this option if you have an existing server - used in development mode, or if your requirements exceed what the - `concatjs_devserver` supports. Be careful that your development round-trip stays - fast (should be under two seconds). - -To use `concatjs_devserver`, you simply `load` the rule, and call it with `deps` that -point to your `ts_library` target(s): - -```python -load("//packages/concatjs:index.bzl", "concatjs_devserver", "ts_library") - -ts_library( - name = "app", - srcs = ["app.ts"], -) - -concatjs_devserver( - name = "devserver", - # We'll collect all the devmode JS sources from these TypeScript libraries - deps = [":app"], - # This is the path we'll request from the browser, see index.html - serving_path = "/bundle.js", - # The devserver can serve our static files too - static_files = ["index.html"], -) -``` - -The `index.html` should be the same one you use for production, and it should -load the JavaScript bundle from the path indicated in `serving_path`. - -If you don't have an index.html file, a simple one will be generated by the -`concatjs_devserver`. - -See `examples/app` in this repository for a working example. To run the -devserver, we recommend you use [ibazel]: - -```sh -$ ibazel run examples/app:devserver -``` - -`ibazel` will keep the devserver program running, and provides a LiveReload -server so the browser refreshes the application automatically when each build -finishes. - -[ibazel]: https://github.com/bazelbuild/bazel-watcher - -## Testing with Karma - -The `karma_web_test` rule runs karma tests with Bazel. - -It depends on rules_webtesting, so you need to add this to your `WORKSPACE` -if you use the web testing rules in `@bazel/concatjs`: - -```python -# Fetch transitive Bazel dependencies of karma_web_test -http_archive( - name = "io_bazel_rules_webtesting", - sha256 = "e9abb7658b6a129740c0b3ef6f5a2370864e102a5ba5ffca2cea565829ed825a", - urls = ["https://github.com/bazelbuild/rules_webtesting/releases/download/0.3.5/rules_webtesting.tar.gz"], -) - -# Set up web testing, choose browsers we can test on -load("@io_bazel_rules_webtesting//web:repositories.bzl", "web_test_repositories") - -web_test_repositories() - -load("@io_bazel_rules_webtesting//web/versioned:browsers-0.3.3.bzl", "browser_repositories") - -browser_repositories( - chromium = True, - firefox = True, -) -``` - -## Known issues with running Chromium for macOS/Windows in Bazel - -For macOS and Windows, Chromium comes with files that contain spaces in their file names. This breaks runfile tree -creation within Bazel due to a bug. There are various workarounds that allow for Chromium on these platforms: - -* Instruct Bazel to automatically disable runfile tree creation if not needed. [More details here](https://github.com/bazelbuild/bazel/issues/4327#issuecomment-922106293) -* Instruct Bazel to use an alternative experimental approach for creating runfile trees. [More details here](https://github.com/bazelbuild/bazel/issues/4327#issuecomment-627422865) - -## Installing with user-managed dependencies - -If you didn't use the `yarn_install` or `npm_install` rule to create an `npm` workspace, you'll have to declare a rule in your root `BUILD.bazel` file to execute karma: - -```python -# Create a karma rule to use in karma_web_test_suite karma -# attribute when using user-managed dependencies -nodejs_binary( - name = "karma/karma", - entry_point = "//:node_modules/karma/bin/karma", - # Point bazel to your node_modules to find the entry point - data = ["//:node_modules"], -) -``` -""" - -load("//packages/concatjs/devserver:concatjs_devserver.bzl", _concatjs_devserver = "concatjs_devserver") -load( - "//packages/concatjs/web_test:karma_web_test.bzl", - _karma_web_test = "karma_web_test", - _karma_web_test_suite = "karma_web_test_suite", -) -load("//packages/concatjs/internal:build_defs.bzl", _ts_library = "ts_library") - -ts_library = _ts_library -karma_web_test = _karma_web_test -karma_web_test_suite = _karma_web_test_suite -concatjs_devserver = _concatjs_devserver
diff --git a/packages/concatjs/index.ts b/packages/concatjs/index.ts deleted file mode 100644 index cf7e68b..0000000 --- a/packages/concatjs/index.ts +++ /dev/null
@@ -1,134 +0,0 @@ -///<amd-module name="@bazel/concatjs"/> -/* - * Concat all JS files before serving. - */ -import * as crypto from 'crypto'; -import * as fs from 'fs'; -import * as File from 'karma/lib/file'; -import * as path from 'path'; -import * as process from 'process'; -import {createInterface} from 'readline'; -///<reference types="lib.dom"/> - -/** - * Return SHA1 of data buffer. - */ -function sha1(data) { - const hash = crypto.createHash('sha1'); - hash.update(data); - return hash.digest('hex'); -} - -/** - * Entry-point for the Karma plugin. - */ -function initConcatJs(logger, emitter, basePath, hostname, port) { - const log = logger.create('framework.concat_js'); - - // Create a tmp file for the concat bundle, rely on Bazel to clean the TMPDIR - const tmpFile = - path.join(process.env['TEST_TMPDIR'], crypto.randomBytes(6).readUIntLE(0, 6).toString(36)); - - emitter.on('file_list_modified', files => { - const bundleFile = new File('/concatjs_bundle.js') as any; - bundleFile.contentPath = tmpFile; - // Preserve all non-JS that were there in the included list. - const included = files.included.filter(f => path.extname(f.originalPath) !== '.js'); - const bundledFiles = - files.included.filter(f => path.extname(f.originalPath) === '.js').map((file) => { - const relativePath = path.relative(basePath, file.originalPath).replace(/\\/g, '/'); - - let content = file.content + `\n//# sourceURL=http://${hostname}:${port}/base/` + - relativePath + '\n'; - - return ` - loadFile( - ${JSON.stringify(relativePath)}, - ${JSON.stringify(content)});`; - }); - - // Execute each file by putting it in a <script> tag. This makes them create - // global variables, even with 'use strict'; (unlike eval). - bundleFile.content = ` -(function() { // Hide local variables - // Use policy to support Trusted Types enforcement. - var policy = null; - if (window.trustedTypes) { - try { - policy = window.trustedTypes.createPolicy('bazel-karma', { - createScript: function(s) { return s; } - }); - } catch (e) { - // In case the policy has been unexpectedly created before, log the error - // and fall back to the old behavior. - console.log(e); - } - } - // IE 8 and below do not support document.head. - var parent = document.getElementsByTagName('head')[0] || - document.documentElement; - function loadFile(path, src) { - var trustedSrc = policy ? policy.createScript(src) : src; - try { - var script = document.createElement('script'); - if ('textContent' in script) { - script.textContent = trustedSrc; - } else { - // This is for IE 8 and below. - script.text = trustedSrc; - } - parent.appendChild(script); - // Don't pollute the DOM with hundreds of <script> tags. - parent.removeChild(script); - } catch(err) { - window.__karma__ && window.__karma__.error( - 'An error occurred while loading ' + path + ':\\n' + - (err.stack || err.message || err.toString())); - console.error('An error occurred while loading ' + path, err); - throw err; - } - } -${bundledFiles.join('')} -})();`; - bundleFile.sha = sha1(Buffer.from(bundleFile.content)); - bundleFile.mtime = new Date(); - included.unshift(bundleFile); - - files.included = included; - files.served.push(bundleFile); - - log.debug('Writing concatjs bundle to tmp file %s', bundleFile.contentPath); - fs.writeFileSync(bundleFile.contentPath, bundleFile.content); - }); -} - -(initConcatJs as any).$inject = - ['logger', 'emitter', 'config.basePath', 'config.hostname', 'config.port']; - -function watcher(fileList: {refresh: () => void}) { - // ibazel will write this string after a successful build - // We don't want to re-trigger tests if the compilation fails, so - // we should only listen for this event. - const IBAZEL_NOTIFY_BUILD_SUCCESS = 'IBAZEL_BUILD_COMPLETED SUCCESS'; - // ibazel communicates with us via stdin - const rl = createInterface({input: process.stdin, terminal: false}); - rl.on('line', (chunk: string) => { - if (chunk === IBAZEL_NOTIFY_BUILD_SUCCESS) { - fileList.refresh(); - } - }); - rl.on('close', () => { - // Give ibazel 5s to kill our process, otherwise do it ourselves - setTimeout(() => { - console.error('ibazel failed to stop karma after 5s; probably a bug'); - process.exit(1); - }, 5000); - }); -} - -(watcher as any).$inject = ['fileList']; - -module.exports = { - 'framework:concat_js': ['factory', initConcatJs], - 'watcher': ['value', watcher], -};
diff --git a/packages/concatjs/internal/BUILD.bazel b/packages/concatjs/internal/BUILD.bazel deleted file mode 100644 index 725a1d2..0000000 --- a/packages/concatjs/internal/BUILD.bazel +++ /dev/null
@@ -1,160 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# gazelle:exclude worker_protocol.proto - -load("@bazel_skylib//:bzl_library.bzl", "bzl_library") -load("//:index.bzl", "nodejs_binary") -load("//packages/jasmine:index.bzl", "jasmine_node_test") -load("//packages/concatjs:index.bzl", "ts_library") -load("@npm//typescript:index.bzl", "tsc") - -package(default_visibility = ["//visibility:public"]) - -bzl_library( - name = "bzl", - srcs = glob( - [ - "*.bzl", - "common/*.bzl", - ], - exclude = ["internal_*"], - ), - visibility = ["//visibility:public"], - deps = [ - "@bazel_skylib//lib:types", - "@build_bazel_rules_nodejs//:bzl", - "@build_bazel_rules_nodejs//internal/common:bzl", - "@rules_nodejs//nodejs:bzl", - ], -) - -exports_files(["tsetse/tsconfig.json"]) - -_TSC_WRAPPED_TESTS = [ - "**/test_support.ts", - "**/*_test.ts", -] - -_TSC_WRAPPED_SRCS = glob( - [ - "tsc_wrapped/*.ts", - "tsetse/**/*.ts", - ], - exclude = _TSC_WRAPPED_TESTS + ["tsetse/tests/**"], -) - -_TSC_WRAPPED_JS = [k[:-3] + ".js" for k in _TSC_WRAPPED_SRCS] - -_TSC_WRAPPED_TYPINGS = [k[:-3] + ".d.ts" for k in _TSC_WRAPPED_SRCS] - -# Build our custom compiler using the vanilla one -tsc( - name = "tsc_wrapped", - outs = _TSC_WRAPPED_JS + _TSC_WRAPPED_TYPINGS, - args = [ - "--declaration", - "-p", - "$(execpath //packages/concatjs/internal:tsconfig.json)", - "--outDir", - "$(RULEDIR)", - ], - data = _TSC_WRAPPED_SRCS + [ - "//packages/concatjs/internal:tsconfig.json", - "@npm//@types/node", - "@npm//@angular/compiler-cli", - "@npm//protobufjs", - "@npm//tsickle", - "@npm//tsutils", - "@npm//typescript", - ], - visibility = ["//visibility:public"], -) - -# Other ts_library rules will use this custom compiler, which calls the -# TypeScript APIs to act like tsc, but adds capabilities like Bazel workers. -# TODO(gregmagolan): make @npm//tsickle dependency optional -nodejs_binary( - name = "tsc_wrapped_bin", - data = [ - ":tsc_wrapped", - "//packages/concatjs/third_party/github.com/bazelbuild/bazel/src/main/protobuf:worker_protocol.proto", - "@npm//protobufjs", - "@npm//source-map-support", - "@npm//tsickle", - "@npm//tsutils", - "@npm//typescript", - ], - entry_point = ":tsc_wrapped/tsc_wrapped.js", - # Disables the Bazel node modules linker. The node module linker is unreliable for the - # persistent worker executable, as it would rely on the `node_modules/` folder in the - # execroot that can be shared in non-sandbox environments or for persistent workers. - # https://docs.bazel.build/versions/main/command-line-reference.html#flag--worker_sandboxing. - templated_args = ["--nobazel_run_linker"], - visibility = ["//visibility:public"], -) - -ts_library( - name = "test_lib", - srcs = glob(_TSC_WRAPPED_TESTS) + _TSC_WRAPPED_TYPINGS, - compiler = "//packages/concatjs/internal:tsc_wrapped_bin", - tsconfig = "//packages/concatjs/internal:tsconfig.json", - deps = [ - "@npm//@angular/compiler-cli", - "@npm//@types/jasmine", - "@npm//@types/node", - "@npm//tsickle", - "@npm//typescript", - ], -) - -jasmine_node_test( - name = "test", - srcs = [], - tags = [ - # TODO: it seems like this test has been broken since we brought in from rules_typescript - "manual", - ], - deps = _TSC_WRAPPED_JS + [ - ":test_lib", - "//packages/concatjs/third_party/github.com/bazelbuild/bazel/src/main/protobuf:worker_protocol.proto", - "@npm//jasmine", - "@npm//protobufjs", - "@npm//source-map", - "@npm//typescript", - ], -) - -# We don't need to distribute any of the content of the BUILD.bazel file in this package -# So generate an empty marker file -genrule( - name = "generated_BUILD", - srcs = [], - # Name the output "BUILD" so it doesn't collide with InputArtifact "BUILD.bazel" - outs = ["BUILD"], - cmd = "echo \"# Marker that this directory is a Bazel package\" > $@", -) - -filegroup( - name = "package_contents", - srcs = [ - "build_defs.bzl", - "common/compilation.bzl", - "common/json_marshal.bzl", - "common/module_mappings.bzl", - "common/tsconfig.bzl", - "ts_config.bzl", - ], - visibility = ["//packages/concatjs:__subpackages__"], -)
diff --git a/packages/concatjs/internal/build_defs.bzl b/packages/concatjs/internal/build_defs.bzl deleted file mode 100644 index 05792c8..0000000 --- a/packages/concatjs/internal/build_defs.bzl +++ /dev/null
@@ -1,538 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"TypeScript compilation" - -load("@rules_nodejs//nodejs:providers.bzl", "LinkablePackageInfo", "js_module_info") -load("@build_bazel_rules_nodejs//:providers.bzl", "ExternalNpmPackageInfo", "js_ecma_script_module_info", "js_named_module_info", "node_modules_aspect", "run_node") - -# pylint: disable=unused-argument -# pylint: disable=missing-docstring -load("//packages/concatjs/internal:common/compilation.bzl", "COMMON_ATTRIBUTES", "DEPS_ASPECTS", "compile_ts", "ts_providers_dict_to_struct") -load("//packages/concatjs/internal:common/tsconfig.bzl", "create_tsconfig") -load("//packages/concatjs/internal:ts_config.bzl", "TsConfigInfo") - -_DOC = """type-check and compile a set of TypeScript sources to JavaScript. - -It produces declarations files (`.d.ts`) which are used for compiling downstream -TypeScript targets and JavaScript for the browser and Closure compiler. - -By default, `ts_library` uses the `tsconfig.json` file in the workspace root -directory. See the notes about the `tsconfig` attribute below. - -## Serving TypeScript for development - -`ts_library` is typically served by the concatjs_devserver rule, documented in the `@bazel/concatjs` package. - -## Accessing JavaScript outputs - -The default output of the `ts_library` rule is the `.d.ts` files. -This is for a couple reasons: - -- help ensure that downstream rules which access default outputs will not require - a cascading re-build when only the implementation changes but not the types -- make you think about whether you want the `devmode` (named `UMD`) or `prodmode` outputs - -You can access the JS output by adding a `filegroup` rule after the `ts_library`, -for example - -```python -ts_library( - name = "compile", - srcs = ["thing.ts"], -) - -filegroup( - name = "thing.js", - srcs = ["compile"], - # Change to es6_sources to get the 'prodmode' JS - output_group = "es5_sources", -) - -my_rule( - name = "uses_js", - deps = ["thing.js"], -) -``` - -""" - -# NB: substituted with "//@bazel/concatjs/bin:tsc_wrapped" in the pkg_npm rule -_DEFAULT_COMPILER = "//packages/concatjs/internal:tsc_wrapped_bin" - -_TYPESCRIPT_TYPINGS = Label( - # BEGIN-INTERNAL - "@npm" + - # END-INTERNAL - "//typescript:typescript__typings", -) - -_TYPESCRIPT_SCRIPT_TARGETS = ["es3", "es5", "es2015", "es2016", "es2017", "es2018", "es2019", "es2020", "esnext"] -_TYPESCRIPT_MODULE_KINDS = ["none", "commonjs", "amd", "umd", "system", "es2015", "esnext"] - -_DEVMODE_TARGET_DEFAULT = "es2015" -_DEVMODE_MODULE_DEFAULT = "umd" -_PRODMODE_TARGET_DEFAULT = "es2015" -_PRODMODE_MODULE_DEFAULT = "esnext" - -def _trim_package_node_modules(package_name): - # trim a package name down to its path prior to a node_modules - # segment. 'foo/node_modules/bar' would become 'foo' and - # 'node_modules/bar' would become '' - segments = [] - for n in package_name.split("/"): - if n == "node_modules": - break - segments.append(n) - return "/".join(segments) - -def _compute_node_modules_root(ctx): - """Computes the node_modules root from the node_modules and deps attributes. - - Args: - ctx: the starlark execution context - - Returns: - The node_modules root as a string - """ - node_modules_root = None - for d in ctx.attr.deps: - if ExternalNpmPackageInfo in d: - possible_root = "/".join(["external", d[ExternalNpmPackageInfo].workspace, "node_modules"]) - if not node_modules_root: - node_modules_root = possible_root - elif node_modules_root != possible_root: - fail("All npm dependencies need to come from a single workspace. Found '%s' and '%s'." % (node_modules_root, possible_root)) - if not node_modules_root: - # there are no fine grained deps but we still need a node_modules_root even if its empty - node_modules_root = "/".join(["external", ctx.attr._typescript_typings[ExternalNpmPackageInfo].workspace, "node_modules"]) - return node_modules_root - -def _filter_ts_inputs(all_inputs): - return [ - f - for f in all_inputs - if f.extension in ["js", "jsx", "ts", "tsx", "json", "proto"] - ] - -def _compile_action(ctx, inputs, outputs, tsconfig_file, node_opts, description = "prodmode"): - externs_files = [] - action_inputs = inputs - action_outputs = [] - for output in outputs: - if output.basename.endswith(".externs.js"): - externs_files.append(output) - elif output.basename.endswith(".es5.MF"): - ctx.actions.write(output, content = "") - else: - action_outputs.append(output) - - # TODO(plf): For now we mock creation of files other than {name}.js. - for externs_file in externs_files: - ctx.actions.write(output = externs_file, content = "") - - # A ts_library that has only .d.ts inputs will have no outputs, - # therefore there are no actions to execute - if not action_outputs: - return None - - action_inputs.extend(_filter_ts_inputs(ctx.attr._typescript_typings[ExternalNpmPackageInfo].sources.to_list())) - - # Also include files from npm fine grained deps as action_inputs. - # These deps are identified by the ExternalNpmPackageInfo provider. - for d in ctx.attr.deps: - if ExternalNpmPackageInfo in d: - # Note: we can't avoid calling .to_list() on sources - action_inputs.extend(_filter_ts_inputs(d[ExternalNpmPackageInfo].sources.to_list())) - - if ctx.file.tsconfig: - action_inputs.append(ctx.file.tsconfig) - if TsConfigInfo in ctx.attr.tsconfig: - action_inputs.extend(ctx.attr.tsconfig[TsConfigInfo].deps) - - # Pass actual options for the node binary in the special "--node_options" argument. - arguments = ["--node_options=%s" % opt for opt in node_opts] - - # We don't try to use the linker to launch the worker process - # because it causes bazel to spawn a new worker for every action - # See https://github.com/bazelbuild/rules_nodejs/issues/1803 - # TODO: understand the interaction between linker and workers better - if ctx.attr.supports_workers: - # One at-sign makes this a params-file, enabling the worker strategy. - # Two at-signs escapes the argument so it's passed through to tsc_wrapped - # rather than the contents getting expanded. - arguments.append("@@" + tsconfig_file.path) - - # Spawn a plain action that runs worker process with no linker - ctx.actions.run( - progress_message = "Compiling TypeScript (%s) %s" % (description, ctx.label), - mnemonic = "TypeScriptCompile", - inputs = action_inputs, - outputs = action_outputs, - # Use the built-in shell environment - # Allow for users who set a custom shell that can locate standard binaries like tr and uname - # See https://github.com/NixOS/nixpkgs/issues/43955#issuecomment-407546331 - use_default_shell_env = True, - arguments = arguments, - executable = ctx.executable.compiler, - execution_requirements = { - "supports-workers": "1", - }, - env = {"COMPILATION_MODE": ctx.var["COMPILATION_MODE"]}, - ) - else: - # TODO: if compiler is vanilla tsc, then we need the '-p' argument too - # arguments.append("-p") - arguments.append(tsconfig_file.path) - - # Run with linker but not as a worker process - run_node( - ctx, - progress_message = "Compiling TypeScript (%s) %s" % (description, ctx.label), - mnemonic = "tsc", - inputs = action_inputs, - outputs = action_outputs, - # Use the built-in shell environment - # Allow for users who set a custom shell that can locate standard binaries like tr and uname - # See https://github.com/NixOS/nixpkgs/issues/43955#issuecomment-407546331 - use_default_shell_env = True, - arguments = arguments, - executable = "compiler", - env = {"COMPILATION_MODE": ctx.var["COMPILATION_MODE"]}, - link_workspace_root = ctx.attr.link_workspace_root, - ) - - # Enable the replay_params in case an aspect needs to re-build this library. - return struct( - label = ctx.label, - tsconfig = tsconfig_file, - inputs = action_inputs, - outputs = action_outputs, - compiler = ctx.executable.compiler, - ) - -def _devmode_compile_action(ctx, inputs, outputs, tsconfig_file, node_opts): - _compile_action( - ctx, - inputs, - outputs, - tsconfig_file, - node_opts, - description = "devmode", - ) - -def tsc_wrapped_tsconfig( - ctx, - files, - srcs, - devmode_manifest = None, - jsx_factory = None, - **kwargs): - """Produce a tsconfig.json that sets options required under Bazel. - - Args: - ctx: the Bazel starlark execution context - files: Labels of all TypeScript compiler inputs - srcs: Immediate sources being compiled, as opposed to transitive deps - devmode_manifest: path to the manifest file to write for --target=es5 - jsx_factory: the setting for tsconfig.json compilerOptions.jsxFactory - **kwargs: remaining args to pass to the create_tsconfig helper - - Returns: - The generated tsconfig.json as an object - """ - - # The location of tsconfig.json is interpreted as the root of the project - # when it is passed to the TS compiler with the `-p` option: - # https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. - # Our tsconfig.json is in bazel-foo/bazel-out/local-fastbuild/bin/{package_path} - # because it's generated in the execution phase. However, our source files are in - # bazel-foo/ and therefore we need to strip some parent directories for each - # f.path. - - node_modules_root = _compute_node_modules_root(ctx) - config = create_tsconfig( - ctx, - # Filter out package.json files that are included in DeclarationInfo - # tsconfig files=[] property should only be .ts/.d.ts - [f for f in files if f.path.endswith(".ts") or f.path.endswith(".tsx")], - srcs, - devmode_manifest = devmode_manifest, - node_modules_root = node_modules_root, - **kwargs - ) - config["bazelOptions"]["nodeModulesPrefix"] = node_modules_root - - # Control target & module via attributes - if devmode_manifest: - # NB: devmode target may still be overriden with a tsconfig bazelOpts.devmodeTargetOverride but that - # configuration settings will be removed in a future major release - config["compilerOptions"]["target"] = ctx.attr.devmode_target if hasattr(ctx.attr, "devmode_target") else _DEVMODE_TARGET_DEFAULT - config["compilerOptions"]["module"] = ctx.attr.devmode_module if hasattr(ctx.attr, "devmode_module") else _DEVMODE_MODULE_DEFAULT - else: - config["compilerOptions"]["target"] = ctx.attr.prodmode_target if hasattr(ctx.attr, "prodmode_target") else _PRODMODE_TARGET_DEFAULT - config["compilerOptions"]["module"] = ctx.attr.prodmode_module if hasattr(ctx.attr, "prodmode_module") else _PRODMODE_MODULE_DEFAULT - - # It's fine for users to have types[] in their tsconfig.json to help the editor - # know which of the node_modules/@types/* entries to include in the program. - # But we don't want TypeScript to do any automatic resolution under tsc_wrapped - # because when not run in a sandbox, it will scan the @types directory and find - # entries that aren't in the action inputs. - # See https://github.com/bazelbuild/rules_typescript/issues/449 - # This setting isn't shared with g3 because there is no node_modules directory there. - config["compilerOptions"]["types"] = [] - - # If the user gives a tsconfig attribute, the generated file should extend - # from the user's tsconfig. - # See https://github.com/Microsoft/TypeScript/issues/9876 - # We subtract the ".json" from the end before handing to TypeScript because - # this gives extra error-checking. - if ctx.file.tsconfig: - workspace_path = config["compilerOptions"]["rootDir"] - config["extends"] = "/".join([workspace_path, ctx.file.tsconfig.path[:-len(".json")]]) - - if jsx_factory: - config["compilerOptions"]["jsxFactory"] = jsx_factory - - return config - -# ************ # -# ts_library # -# ************ # - -def _ts_library_impl(ctx): - """Implementation of ts_library. - - Args: - ctx: the context. - - Returns: - the struct returned by the call to compile_ts. - """ - ts_providers = compile_ts( - ctx, - is_library = True, - compile_action = _compile_action, - devmode_compile_action = _devmode_compile_action, - tsc_wrapped_tsconfig = tsc_wrapped_tsconfig, - ) - - # Add in shared JS providers. - # See design doc https://docs.google.com/document/d/1ggkY5RqUkVL4aQLYm7esRW978LgX3GUCnQirrk5E1C0/edit# - # and issue https://github.com/bazelbuild/rules_nodejs/issues/57 for more details. - ts_providers["providers"].extend([ - js_module_info( - sources = ts_providers["typescript"]["es5_sources"], - deps = ctx.attr.deps, - ), - js_named_module_info( - sources = ts_providers["typescript"]["es5_sources"], - deps = ctx.attr.deps, - ), - js_ecma_script_module_info( - sources = ts_providers["typescript"]["es6_sources"], - deps = ctx.attr.deps, - ), - # TODO: remove legacy "typescript" provider - # once it is no longer needed. - ]) - - if ctx.attr.package_name: - link_path = "/".join([p for p in [ctx.bin_dir.path, ctx.label.workspace_root, ctx.label.package] if p]) - ts_providers["providers"].append(LinkablePackageInfo( - package_name = ctx.attr.package_name, - package_path = ctx.attr.package_path, - path = link_path, - files = ts_providers["typescript"]["es5_sources"], - )) - - return ts_providers_dict_to_struct(ts_providers) - -ts_library = rule( - _ts_library_impl, - attrs = dict(COMMON_ATTRIBUTES, **{ - "angular_assets": attr.label_list( - doc = """Additional files the Angular compiler will need to read as inputs. - Includes .css and .html files""", - allow_files = [".css", ".html"], - ), - "compiler": attr.label( - doc = """Sets a different TypeScript compiler binary to use for this library. -For example, we use the vanilla TypeScript tsc.js for bootstrapping, -and Angular compilations can replace this with `ngc`. - -The default ts_library compiler depends on the `//@bazel/typescript` -target which is setup for projects that use bazel managed npm deps and -install the @bazel/typescript npm package. - -You can also use a custom compiler to increase the NodeJS heap size used for compilations. - -To do this, declare your own binary for running `tsc_wrapped`, e.g.: - -```python -nodejs_binary( - name = "tsc_wrapped_bin", - entry_point = "@npm//:node_modules/@bazel/typescript/internal/tsc_wrapped/tsc_wrapped.js", - templated_args = [ - "--node_options=--max-old-space-size=2048", - ], - data = [ - "@npm//protobufjs", - "@npm//source-map-support", - "@npm//tsutils", - "@npm//typescript", - "@npm//@bazel/typescript", - ], -) -``` - -then refer to that target in the `compiler` attribute. - -Note that `nodejs_binary` targets generated by `npm_install`/`yarn_install` can include data dependencies -on packages which aren't declared as dependencies. -For example, if you use [tsickle](https://github.com/angular/tsickle) to generate Closure Compiler-compatible JS, -then it needs to be a data dependency of `tsc_wrapped` so that it can be loaded at runtime. -""", - default = Label(_DEFAULT_COMPILER), - allow_files = True, - executable = True, - cfg = "exec", - ), - "deps": attr.label_list( - aspects = DEPS_ASPECTS + [node_modules_aspect], - doc = "Compile-time dependencies, typically other ts_library targets", - ), - "devmode_module": attr.string( - doc = """Set the typescript `module` compiler option for devmode output. - -This value will override the `module` option in the user supplied tsconfig.""", - values = _TYPESCRIPT_MODULE_KINDS, - default = _DEVMODE_MODULE_DEFAULT, - ), - "devmode_target": attr.string( - doc = """Set the typescript `target` compiler option for devmode output. - -This value will override the `target` option in the user supplied tsconfig.""", - values = _TYPESCRIPT_SCRIPT_TARGETS, - default = _DEVMODE_TARGET_DEFAULT, - ), - "internal_testing_type_check_dependencies": attr.bool(default = False, doc = "Testing only, whether to type check inputs that aren't srcs."), - "link_workspace_root": attr.bool( - doc = """Link the workspace root to the bin_dir to support absolute requires like 'my_wksp/path/to/file'. - -If source files need to be required then they can be copied to the bin_dir with copy_to_bin.""", - ), - "package_name": attr.string( - doc = """The package name that the linker will link this ts_library output as. - -If package_path is set, the linker will link this package under <package_path>/node_modules/<package_name>. -If package_path is not set the this will be the root node_modules of the workspace.""", - ), - "package_path": attr.string( - doc = """The package path in the workspace that the linker will link this ts_library output to. - -If package_path is set, the linker will link this package under <package_path>/node_modules/<package_name>. -If package_path is not set the this will be the root node_modules of the workspace.""", - ), - "prodmode_module": attr.string( - doc = """Set the typescript `module` compiler option for prodmode output. - -This value will override the `module` option in the user supplied tsconfig.""", - values = _TYPESCRIPT_MODULE_KINDS, - default = _PRODMODE_MODULE_DEFAULT, - ), - "prodmode_target": attr.string( - doc = """Set the typescript `target` compiler option for prodmode output. - -This value will override the `target` option in the user supplied tsconfig.""", - values = _TYPESCRIPT_SCRIPT_TARGETS, - default = _PRODMODE_TARGET_DEFAULT, - ), - "srcs": attr.label_list( - doc = "The TypeScript source files to compile.", - allow_files = [".ts", ".tsx"], - mandatory = True, - ), - "supports_workers": attr.bool( - doc = """Intended for internal use only. - -Allows you to disable the Bazel Worker strategy for this library. -Typically used together with the "compiler" setting when using a -non-worker aware compiler binary.""", - default = True, - ), - - # TODO(alexeagle): reconcile with google3: ts_library rules should - # be portable across internal/external, so we need this attribute - # internally as well. - "tsconfig": attr.label( - doc = """A tsconfig.json file containing settings for TypeScript compilation. -Note that some properties in the tsconfig are governed by Bazel and will be -overridden, such as `target` and `module`. - -The default value is set to `//:tsconfig.json` by a macro. This means you must -either: - -- Have your `tsconfig.json` file in the workspace root directory -- Use an alias in the root BUILD.bazel file to point to the location of tsconfig: - `alias(name="tsconfig.json", actual="//path/to:tsconfig-something.json")` - and also make the tsconfig.json file visible to other Bazel packages: - `exports_files(["tsconfig.json"], visibility = ["//visibility:public"])` -- Give an explicit `tsconfig` attribute to all `ts_library` targets - """, - allow_single_file = True, - ), - "tsickle_typed": attr.bool( - default = True, - doc = "If using tsickle, instruct it to translate types to ClosureJS format", - ), - "use_angular_plugin": attr.bool( - doc = """Run the Angular ngtsc compiler under ts_library""", - ), - "_typescript_typings": attr.label( - default = _TYPESCRIPT_TYPINGS, - ), - }), - outputs = { - "tsconfig": "%{name}_tsconfig.json", - }, - doc = _DOC, -) - -def ts_library_macro(tsconfig = None, **kwargs): - """Wraps `ts_library` to set the default for the `tsconfig` attribute. - - This must be a macro so that the string is converted to a label in the context of the - workspace that declares the `ts_library` target, rather than the workspace that defines - `ts_library`, or the workspace where the build is taking place. - - This macro is re-exported as `ts_library` in the public API. - - Args: - tsconfig: the label pointing to a tsconfig.json file - **kwargs: remaining args to pass to the ts_library rule - """ - if not tsconfig: - tsconfig = "//:tsconfig.json" - - # plugins generally require the linker - # (unless the user statically linked them into the compiler binary) - # Therefore we disable workers for them by default - if "supports_workers" in kwargs.keys(): - supports_workers = kwargs.pop("supports_workers") - else: - uses_plugin = kwargs.get("use_angular_plugin", False) - supports_workers = not uses_plugin - - ts_library(tsconfig = tsconfig, supports_workers = supports_workers, **kwargs)
diff --git a/packages/concatjs/internal/common/compilation.bzl b/packages/concatjs/internal/common/compilation.bzl deleted file mode 100644 index fed787a..0000000 --- a/packages/concatjs/internal/common/compilation.bzl +++ /dev/null
@@ -1,534 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Used for compilation by the different implementations of build_defs.bzl. -""" - -load("@rules_nodejs//nodejs:providers.bzl", "DeclarationInfo") -load(":common/json_marshal.bzl", "json_marshal") -load(":common/module_mappings.bzl", "module_mappings_aspect") - -_DEBUG = False - -DEPS_ASPECTS = [ - module_mappings_aspect, -] - -_ADDITIONAL_D_TS = attr.label_list( - allow_files = True, -) - -# Mock out the JsInfo blaze-only provider -JsInfo = provider() - -# Attributes shared by any typescript-compatible rule (ts_library, ng_module) -# @unsorted-dict-items -COMMON_ATTRIBUTES = { - "data": attr.label_list( - default = [], - allow_files = True, - ), - # A list of diagnostics expected when compiling this library, in the form of - # "diagnostic:regexp", e.g. "TS1234:failed to quizzle the .* wobble". - # Useful to test for expected compilation errors. - "expected_diagnostics": attr.string_list(), - # Whether to generate externs.js from any "declare" statement. - "generate_externs": attr.bool(default = True), - # Used to determine module mappings - "module_name": attr.string(), - "module_root": attr.string(), - # TODO(evanm): make this the default and remove the option. - "runtime": attr.string(default = "browser"), - # TODO(radokirov): remove this attr when clutz is stable enough to consume - # any closure JS code. - "runtime_deps": attr.label_list( - default = [], - providers = [JsInfo], - ), - "deps": attr.label_list( - aspects = DEPS_ASPECTS, - providers = [DeclarationInfo], - ), - "_additional_d_ts": _ADDITIONAL_D_TS, -} - -# Attributes shared by any typescript-compatible aspect. -ASPECT_ATTRIBUTES = { - "_additional_d_ts": _ADDITIONAL_D_TS, -} - -COMMON_OUTPUTS = { - # Allow the tsconfig.json to be generated without running compile actions. - "tsconfig": "%{name}_tsconfig.json", -} - -_DEPSET_TYPE = type(depset()) - -def _collect_dep_declarations(ctx, declaration_infos): - """Flattens DeclarationInfo from typescript and javascript dependencies. - - Args: - ctx: ctx. - declaration_infos: list of DeclarationInfo collected from dependent targets - - Returns: - A struct of depsets for direct, transitive and type-blacklisted declarations. - """ - - # .d.ts files from direct dependencies, ok for strict deps - direct_deps_declarations = [dep.declarations for dep in declaration_infos] - - # all reachable .d.ts files from dependencies. - transitive_deps_declarations = [dep.transitive_declarations for dep in declaration_infos] - - # all reachable .d.ts files from node_modules attribute (if it has a typescript provider) - # "node_modules" still checked for backward compat for ng_module - if hasattr(ctx.attr, "node_modules"): - if DeclarationInfo in ctx.attr.node_modules: - transitive_deps_declarations.append(ctx.attr.node_modules[DeclarationInfo].transitive_declarations) - elif hasattr(ctx.attr.node_modules, "typescript"): - # TODO(b/139705078): remove this case after bazel BUILD file generation for node_modules is updated - transitive_deps_declarations.append([ctx.attr.node_modules.typescript.transitive_declarations]) - if hasattr(ctx.attr, "_typescript_typings"): - transitive_deps_declarations.append(ctx.attr._typescript_typings[DeclarationInfo].transitive_declarations) - - # .d.ts files whose types tsickle will not emit (used for ts_declaration(generate_externs=False). - type_blocklisted_declarations = [dep.type_blocklisted_declarations for dep in declaration_infos] - - # If a tool like github.com/angular/clutz can create .d.ts from type annotated .js - # its output will be collected here. - - return DeclarationInfo( - declarations = depset(transitive = direct_deps_declarations), - transitive_declarations = depset(ctx.files._additional_d_ts, transitive = transitive_deps_declarations), - type_blocklisted_declarations = depset(transitive = type_blocklisted_declarations), - ) - -def _should_generate_externs(ctx): - """Whether externs should be generated. - - If ctx has a generate_externs attribute, the value of that is returned. - Otherwise, this is true.""" - return getattr(ctx.attr, "generate_externs", True) - -def _get_runtime(ctx): - """Gets the runtime for the rule. - - Defaults to "browser" if the runtime attr isn't present.""" - return getattr(ctx.attr, "runtime", "browser") - -def _outputs(ctx, label, srcs_files = []): - """Returns closure js, devmode js, and .d.ts output files. - - Args: - ctx: ctx. - label: Label. package label. - srcs_files: File list. sources files list. - - Returns: - A struct of file lists for different output types and their relationship to each other. - """ - workspace_segments = label.workspace_root.split("/") if label.workspace_root else [] - package_segments = label.package.split("/") if label.package else [] - trim = len(workspace_segments) + len(package_segments) - create_shim_files = False - - closure_js_files = [] - devmode_js_files = [] - declaration_files = [] - transpilation_infos = [] - for input_file in srcs_files: - is_dts = input_file.short_path.endswith(".d.ts") - if is_dts and not create_shim_files: - continue - basename = "/".join(input_file.short_path.split("/")[trim:]) - for ext in [".d.ts", ".tsx", ".ts"]: - if basename.endswith(ext): - basename = basename[:-len(ext)] - break - closure_js_file = ctx.actions.declare_file(basename + ".mjs") - closure_js_files.append(closure_js_file) - - # Temporary until all imports of ngfactory/ngsummary files are removed - # TODO(alexeagle): clean up after Ivy launch - if getattr(ctx.attr, "use_angular_plugin", False): - closure_js_files.append(ctx.actions.declare_file(basename + ".ngfactory.mjs")) - closure_js_files.append(ctx.actions.declare_file(basename + ".ngsummary.mjs")) - - if not is_dts: - devmode_js_file = ctx.actions.declare_file(basename + ".js") - devmode_js_files.append(devmode_js_file) - transpilation_infos.append(struct(closure = closure_js_file, devmode = devmode_js_file)) - declaration_files.append(ctx.actions.declare_file(basename + ".d.ts")) - - # Temporary until all imports of ngfactory/ngsummary files are removed - # TODO(alexeagle): clean up after Ivy launch - if getattr(ctx.attr, "use_angular_plugin", False): - devmode_js_files.append(ctx.actions.declare_file(basename + ".ngfactory.js")) - devmode_js_files.append(ctx.actions.declare_file(basename + ".ngsummary.js")) - declaration_files.append(ctx.actions.declare_file(basename + ".ngfactory.d.ts")) - declaration_files.append(ctx.actions.declare_file(basename + ".ngsummary.d.ts")) - return struct( - closure_js = closure_js_files, - devmode_js = devmode_js_files, - declarations = declaration_files, - transpilation_infos = transpilation_infos, - ) - -def compile_ts( - ctx, - is_library, - srcs = None, - declaration_infos = None, - compile_action = None, - devmode_compile_action = None, - jsx_factory = None, - tsc_wrapped_tsconfig = None, - tsconfig = None, - outputs = _outputs, - validation_outputs = None): - """Creates actions to compile TypeScript code. - - This rule is shared between ts_library and ts_declaration. - - Args: - ctx: ctx. - is_library: boolean. False if only compiling .dts files. - srcs: label list. Explicit list of sources to be used instead of ctx.attr.srcs. - declaration_infos: list of DeclarationInfo. Explicit list of declarations to be used instead of those on ctx.attr.deps. - compile_action: function. Creates the compilation action. - devmode_compile_action: function. Creates the compilation action - for devmode. - jsx_factory: optional string. Enables overriding jsx pragma. - tsc_wrapped_tsconfig: function that produces a tsconfig object. - tsconfig: The tsconfig file to output, if other than ctx.outputs.tsconfig. - outputs: function from a ctx to the expected compilation outputs. - validation_outputs: Actions that produce validation outputs will be run whenever any part of - a rule is run, even if its outputs are not used. This is useful for things like strict deps check. - - Returns: - struct that will be returned by the rule implementation. - """ - - ### Collect srcs and outputs. - srcs = srcs if srcs != None else ctx.attr.srcs - if declaration_infos == None: - if not hasattr(ctx.attr, "deps"): - fail("compile_ts must either be called from a rule with a deps attr, or must be given declaration_infos") - - # By default, we collect dependencies from the ctx, when used as a rule - declaration_infos = [ - d[DeclarationInfo] - for d in ctx.attr.deps + getattr(ctx.attr, "_helpers", []) - ] - - tsconfig = tsconfig if tsconfig != None else ctx.outputs.tsconfig - srcs_files = [f for t in srcs for f in t.files.to_list()] - src_declarations = [] # d.ts found in inputs. - tsickle_externs = [] # externs.js generated by tsickle, if any. - has_sources = False - - for src in srcs: - if src.label.package != ctx.label.package: - # Sources can be in sub-folders, but not in sub-packages. - fail("Sources must be in the same package as the ts_library rule, " + - "but %s is not in %s" % (src.label, ctx.label.package), "srcs") - if hasattr(src, "typescript"): - # Guard against users accidentally putting deps into srcs by - # rejecting all srcs values that have a TypeScript provider. - # TS rules produce a ".d.ts" file, which is a valid input in "srcs", - # and will then be compiled as a source .d.ts file would, creating - # externs etc. - fail( - "must not reference any TypeScript rules - did you mean deps?", - "srcs", - ) - - for f in src.files.to_list(): - has_sources = True - if not is_library and not f.path.endswith(".d.ts"): - fail("srcs must contain only type declarations (.d.ts files), " + - "but %s contains %s" % (src.label, f.short_path), "srcs") - if f.path.endswith(".d.ts"): - src_declarations.append(f) - continue - - outs = outputs(ctx, ctx.label, srcs_files) - transpiled_closure_js = outs.closure_js - transpiled_devmode_js = outs.devmode_js - gen_declarations = outs.declarations - - # Not all existing implementations of outputs() may return transpilation_infos - transpilation_infos = getattr(outs, "transpilation_infos", []) - - if has_sources and _get_runtime(ctx) != "nodejs": - # Note: setting this variable controls whether tsickle is run at all. - tsickle_externs = [ctx.actions.declare_file(ctx.label.name + ".externs.js")] - - dep_declarations = _collect_dep_declarations(ctx, declaration_infos) - - type_blocklisted_declarations = dep_declarations.type_blocklisted_declarations - if not is_library and not _should_generate_externs(ctx): - type_blocklisted_declarations = depset(srcs_files, transitive = [type_blocklisted_declarations]) - - # The depsets of output files. These are the files that are always built - # (including e.g. if you "blaze build :the_target" directly). - files_depsets = [] - - # A manifest listing the order of this rule's *.ts files (non-transitive) - # Only generated if the rule has any sources. - devmode_manifest = None - - # Enable to produce a performance trace when compiling TypeScript to JS. - # The trace file location will be printed as a build result and can be read - # in Chrome's chrome://tracing/ UI. - perf_trace = _DEBUG - if "TYPESCRIPT_PERF_TRACE_TARGET" in ctx.var: - perf_trace = str(ctx.label) == ctx.var["TYPESCRIPT_PERF_TRACE_TARGET"] - - compilation_inputs = dep_declarations.transitive_declarations.to_list() + srcs_files - tsickle_externs_path = tsickle_externs[0] if tsickle_externs else None - - # Calculate allowed dependencies for strict deps enforcement. - allowed_deps = depset( - # A target's sources may depend on each other, - srcs_files, - # or on a .d.ts from a direct dependency - transitive = [dep_declarations.declarations], - ) - - tsconfig_es6 = tsc_wrapped_tsconfig( - ctx, - compilation_inputs, - srcs_files, - jsx_factory = jsx_factory, - tsickle_externs = tsickle_externs_path, - type_blocklisted_declarations = type_blocklisted_declarations.to_list(), - allowed_deps = allowed_deps, - ) - - # Do not produce declarations in ES6 mode, tsickle cannot produce correct - # .d.ts (or even errors) from the altered Closure-style JS emit. - tsconfig_es6["compilerOptions"]["declaration"] = False - tsconfig_es6["compilerOptions"].pop("declarationDir") - outputs = transpiled_closure_js + tsickle_externs - - node_profile_args = [] - if perf_trace and has_sources: - perf_trace_file = ctx.actions.declare_file(ctx.label.name + ".es6.trace") - tsconfig_es6["bazelOptions"]["perfTracePath"] = perf_trace_file.path - outputs.append(perf_trace_file) - - profile_file = ctx.actions.declare_file(ctx.label.name + ".es6.v8.log") - node_profile_args = [ - "--prof", - # Without nologfile_per_isolate, v8 embeds an - # unpredictable hash code in the file name, which - # doesn't work with blaze. - "--nologfile_per_isolate", - "--logfile=" + profile_file.path, - ] - outputs.append(profile_file) - - files_depsets.append(depset([perf_trace_file, profile_file])) - - ctx.actions.write( - output = tsconfig, - content = json_marshal(tsconfig_es6), - ) - - # Parameters of this compiler invocation in case we need to replay this with different - # settings. - replay_params = None - - if has_sources: - inputs = compilation_inputs + [tsconfig] + getattr(ctx.files, "angular_assets", []) - replay_params = compile_action( - ctx, - inputs, - outputs, - tsconfig, - node_profile_args, - ) - - devmode_manifest = ctx.actions.declare_file(ctx.label.name + ".es5.MF") - tsconfig_json_es5 = ctx.actions.declare_file(ctx.label.name + "_es5_tsconfig.json") - outputs = ( - transpiled_devmode_js + gen_declarations + [devmode_manifest] - ) - tsconfig_es5 = tsc_wrapped_tsconfig( - ctx, - compilation_inputs, - srcs_files, - jsx_factory = jsx_factory, - devmode_manifest = devmode_manifest.path, - allowed_deps = allowed_deps, - ) - node_profile_args = [] - if perf_trace: - perf_trace_file = ctx.actions.declare_file(ctx.label.name + ".es5.trace") - tsconfig_es5["bazelOptions"]["perfTracePath"] = perf_trace_file.path - outputs.append(perf_trace_file) - - profile_file = ctx.actions.declare_file(ctx.label.name + ".es5.v8.log") - node_profile_args = [ - "--prof", - # Without nologfile_per_isolate, v8 embeds an - # unpredictable hash code in the file name, which - # doesn't work with blaze. - "--nologfile_per_isolate", - "--logfile=" + profile_file.path, - ] - outputs.append(profile_file) - - files_depsets.append(depset([perf_trace_file, profile_file])) - - ctx.actions.write(output = tsconfig_json_es5, content = json_marshal( - tsconfig_es5, - )) - devmode_compile_action( - ctx, - compilation_inputs + [tsconfig_json_es5] + getattr(ctx.files, "angular_assets", []), - outputs, - tsconfig_json_es5, - node_profile_args, - ) - - # TODO(martinprobst): Merge the generated .d.ts files, and enforce strict - # deps (do not re-export transitive types from the transitive closure). - transitive_decls = depset(src_declarations + gen_declarations, transitive = [dep_declarations.transitive_declarations]) - - # both ts_library and ts_declarations generate .mjs files: - # - for libraries, this is the ES6/production code - # - for declarations, these are generated shims - es6_sources = depset(transpiled_closure_js + tsickle_externs) - if is_library: - es5_sources = depset(transpiled_devmode_js) - else: - # In development mode, no code ever references shims as they only - # contain types, and the ES5 code does not get type annotated. - es5_sources = depset(tsickle_externs) - - # Similarly, in devmode these sources do not get loaded, so do not need - # to be in a manifest. - devmode_manifest = None - - # Downstream rules see the .d.ts files produced or declared by this rule. - declarations_depsets = [depset(gen_declarations + src_declarations)] - if not srcs_files: - # Re-export sources from deps. - # TODO(b/30018387): introduce an "exports" attribute. - for dep in declaration_infos: - declarations_depsets.append(dep.declarations) - files_depsets.extend(declarations_depsets) - - # If this is a ts_declaration, add tsickle_externs to the outputs list to - # force compilation of d.ts files. (tsickle externs are produced by running a - # compilation over the d.ts file and extracting type information.) - if not is_library: - files_depsets.append(depset(tsickle_externs)) - - transitive_es6_sources_sets = [es6_sources] - for dep in getattr(ctx.attr, "deps", []): - if hasattr(dep, "typescript"): - transitive_es6_sources_sets.append(dep.typescript.transitive_es6_sources) - transitive_es6_sources = depset(transitive = transitive_es6_sources_sets) - - declarations_provider = DeclarationInfo( - declarations = depset(transitive = declarations_depsets), - transitive_declarations = transitive_decls, - type_blocklisted_declarations = type_blocklisted_declarations, - ) - - # @unsorted-dict-items - return { - "providers": [ - DefaultInfo( - runfiles = ctx.runfiles( - # Note: don't include files=... here, or they will *always* be built - # by any dependent rule, regardless of whether it needs them. - # But these attributes are needed to pass along any input runfiles: - collect_default = True, - collect_data = True, - ), - files = depset(transitive = files_depsets), - ), - OutputGroupInfo( - _validation = depset(validation_outputs if validation_outputs else []), - es5_sources = es5_sources, - es6_sources = es6_sources, - ), - declarations_provider, - ], - # Also expose the DeclarationInfo as a named provider so that aspect implementations can reference it - # Otherwise they would be forced to reference it by a numeric index out of the "providers" list above. - "declarations": declarations_provider, - "instrumented_files": { - "dependency_attributes": ["deps", "runtime_deps"], - "extensions": ["ts", "tsx"], - "source_attributes": ["srcs"], - }, - # Expose the module_name so that packaging rules can access it. - # e.g. rollup_bundle under Bazel needs to convert this into a UMD global - # name in the Rollup configuration. - "module_name": getattr(ctx.attr, "module_name", None), - # Expose the tags so that a Bazel aspect can access them. - "tags": ctx.attr.tags if hasattr(ctx.attr, "tags") else ctx.rule.attr.tags, - # @unsorted-dict-items - "typescript": { - "devmode_manifest": devmode_manifest, - "es5_sources": es5_sources, - "es6_sources": es6_sources, - "replay_params": replay_params, - "transitive_es6_sources": transitive_es6_sources, - "tsickle_externs": tsickle_externs, - "transpilation_infos": transpilation_infos, - }, - } - -def ts_providers_dict_to_struct(d): - """ Converts a dict to a struct, recursing into a single level of nested dicts. - - This allows users of compile_ts to modify or augment the returned dict before - converting it to an immutable struct. - - Args: - d: the dict to convert - - Returns: - An immutable struct created from the input dict - """ - - # These keys are present in the dict so that aspects can reference them, - # however they should not be output as legacy providers since we have modern - # symbol-typed providers for them. - js_provider = d.pop("js", None) - declarations_provider = d.pop("declarations", None) - - # Promote the "js" string-typed provider to a modern provider - if js_provider: - # Create a new providers list rather than modify the existing list - d["providers"] = d.get("providers", []) + [js_provider] - - for key, value in d.items(): - if key != "output_groups" and type(value) == type({}): - d[key] = struct(**value) - result = struct(**d) - - # Restore the elements we removed, to avoid side-effect of mutating the argument - if js_provider: - d["js"] = js_provider - if declarations_provider: - d["declarations"] = declarations_provider - return result
diff --git a/packages/concatjs/internal/common/json_marshal.bzl b/packages/concatjs/internal/common/json_marshal.bzl deleted file mode 100644 index 0199c45..0000000 --- a/packages/concatjs/internal/common/json_marshal.bzl +++ /dev/null
@@ -1,36 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Marshal an arbitrary Starlark object to JSON.""" - -def json_marshal(data): - """Serializes arbitrary data to JSON. - - Args: - data: any object - - Returns: - JSON string representing the data - """ - if type(data) == "dict" or type(data) == "list": - return str(data).replace(": True", ": true").replace(": False", ": false").replace(": None", ": false") - elif type(data) == "int": - return str(data) - elif type(data) == "string": - return "\"" + data + "\"" - elif type(data) == "Label": - return "\"//{}:{}\"".format(data.package, data.name) - elif type(data) == "bool": - return "true" if data else "false" - return "unknown type {}: {}".format(type(data), data)
diff --git a/packages/concatjs/internal/common/json_marshal_test.bzl b/packages/concatjs/internal/common/json_marshal_test.bzl deleted file mode 100644 index a83151c..0000000 --- a/packages/concatjs/internal/common/json_marshal_test.bzl +++ /dev/null
@@ -1,33 +0,0 @@ -"""Unit tests for json marshaling. - -Note, this cannot live next to the file it tests, because that file is in -third_party bazel rules, and bazel doesn't support starlark testing yet. -""" - -load("//third_party/bazel_rules/rules_typescript/internal:common/json_marshal.bzl", "json_marshal") -load("//third_party/bazel_skylib/lib:unittest.bzl", "asserts", "unittest") - -def _test_impl(ctx): - env = unittest.begin(ctx) - asserts.equals(env, "\"abc\"", json_marshal("abc")) - asserts.equals(env, "123", json_marshal(123)) - asserts.equals(env, "true", json_marshal(True)) - asserts.equals(env, "false", json_marshal(False)) - asserts.equals(env, "\"//a:b\"", json_marshal(Label("//a:b"))) - asserts.equals(env, "[]", json_marshal([])) - asserts.equals(env, "{}", json_marshal({})) - asserts.equals(env, """[1, 2, 3]""", json_marshal([1, 2, 3])) - asserts.equals(env, """{"a": "b"}""", json_marshal({"a": "b"})) - asserts.equals(env, """{"none": false}""", json_marshal({"none": None})) - asserts.equals( - env, - """{"a": {"d": 1, "e": true, "f": ["f1", "f2"]}, "b": "val", "c": [{"g": false}]}""", - json_marshal({"a": {"d": 1, "e": True, "f": ["f1", "f2"]}, "b": "val", "c": [{"g": False}]}), - ) - - return unittest.end(env) - -_test = unittest.make(_test_impl) - -def json_marshal_test_suite(): - unittest.suite("json_marshal_tests", _test)
diff --git a/packages/concatjs/internal/common/module_mappings.bzl b/packages/concatjs/internal/common/module_mappings.bzl deleted file mode 100644 index a4aedb7..0000000 --- a/packages/concatjs/internal/common/module_mappings.bzl +++ /dev/null
@@ -1,144 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Definitions for handling path re-mapping, to support short module names. -# See pathMapping doc: https://github.com/Microsoft/TypeScript/issues/5039 -# -# This reads the module_root and module_name attributes from typescript rules in -# the transitive closure, rolling these up to provide a mapping to the -# TypeScript compiler and to editors. -# -"""Module mappings. -""" - -def _get_deps(attrs, names): - return [ - d - for n in names - if hasattr(attrs, n) - for d in getattr(attrs, n) - ] - -# Traverse 'srcs' in addition so that we can go across a genrule -_MODULE_MAPPINGS_DEPS_NAMES = ["deps", "srcs", "_helpers"] - -_DEBUG = False - -def debug(msg, values = ()): - if _DEBUG: - print(msg % values) - -def get_module_mappings(label, attrs, srcs = [], workspace_name = None, mappings_attr = "es6_module_mappings"): - """Returns the module_mappings from the given attrs. - - Collects a {module_name - module_root} hash from all transitive dependencies, - checking for collisions. If a module has a non-empty `module_root` attribute, - all sources underneath it are treated as if they were rooted at a folder - `module_name`. - - Args: - label: The label declaring a module mapping - attrs: Attributes on that label - srcs: The srcs attribute, used to validate that these are under the root - workspace_name: name of the workspace where the user is building - mappings_attr: name of the attribute we use to hand down transitive data - - Returns: - the module_mappings from the given attrs. - """ - mappings = dict() - all_deps = _get_deps(attrs, names = _MODULE_MAPPINGS_DEPS_NAMES) - for dep in all_deps: - if not hasattr(dep, mappings_attr): - continue - for k, v in getattr(dep, mappings_attr).items(): - if k in mappings and mappings[k] != v: - fail(("duplicate module mapping at %s: %s maps to both %s and %s" % - (label, k, mappings[k], v)), "deps") - mappings[k] = v - if ((hasattr(attrs, "module_name") and attrs.module_name) or - (hasattr(attrs, "module_root") and attrs.module_root)): - mn = attrs.module_name - if not mn: - mn = label.name - mr = "/".join([p for p in [ - workspace_name or label.workspace_root, - label.package, - ] if p]) - if hasattr(attrs, "module_root") and attrs.module_root and attrs.module_root != ".": - mr = "%s/%s" % (mr, attrs.module_root) - if attrs.module_root.endswith(".ts"): - if workspace_name: - mr = mr.replace(".d.ts", "") - - # Validate that sources are underneath the module root. - # module_roots ending in .ts are a special case, they are used to - # restrict what's exported from a build rule, e.g. only exports from a - # specific index.d.ts file. For those, not every source must be under the - # given module root. - - else: - for s in srcs: - short_path = s.short_path - - # Execroot paths for external repositories should start with external/ - # But the short_path property of file gives the relative path from our workspace - # instead. We must correct this to compare with the module_root which is an - # execroot path. - if short_path.startswith("../"): - short_path = "external/" + short_path[3:] - if not short_path.startswith(mr): - fail(("all sources must be under module root: %s, but found: %s" % - (mr, short_path))) - if mn in mappings and mappings[mn] != mr: - fail(("duplicate module mapping at %s: %s maps to both %s and %s" % - (label, mn, mappings[mn], mr)), "deps") - mappings[mn] = mr - - debug("Mappings at %s: %s", (label, mappings)) - return mappings - -def _module_mappings_aspect_impl(target, ctx): - mappings = get_module_mappings(target.label, ctx.rule.attr) - return struct(es6_module_mappings = mappings) - -module_mappings_aspect = aspect( - _module_mappings_aspect_impl, - attr_aspects = _MODULE_MAPPINGS_DEPS_NAMES, -) - -# When building a mapping for use at runtime, we need paths to be relative to -# the runfiles directory. This requires the workspace_name to be prefixed on -# each module root. -def _module_mappings_runtime_aspect_impl(target, ctx): - if target.label.workspace_root: - # We need the workspace_name for the target being visited. - # Starlark doesn't have this - instead they have a workspace_root - # which looks like "external/repo_name" - so grab the second path segment. - # TODO(alexeagle): investigate a better way to get the workspace name - workspace_name = target.label.workspace_root.split("/")[1] - else: - workspace_name = ctx.workspace_name - mappings = get_module_mappings( - target.label, - ctx.rule.attr, - workspace_name = workspace_name, - mappings_attr = "runfiles_module_mappings", - ) - return struct(runfiles_module_mappings = mappings) - -module_mappings_runtime_aspect = aspect( - _module_mappings_runtime_aspect_impl, - attr_aspects = _MODULE_MAPPINGS_DEPS_NAMES, -)
diff --git a/packages/concatjs/internal/common/tsconfig.bzl b/packages/concatjs/internal/common/tsconfig.bzl deleted file mode 100644 index b01c999..0000000 --- a/packages/concatjs/internal/common/tsconfig.bzl +++ /dev/null
@@ -1,306 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Helpers for configuring the TypeScript compiler. -""" - -load(":common/module_mappings.bzl", "get_module_mappings") - -_DEBUG = False - -def create_tsconfig( - ctx, - files, - srcs, - devmode_manifest = None, - tsickle_externs = None, - type_blocklisted_declarations = [], - out_dir = None, - disable_strict_deps = False, - allowed_deps = depset(), - extra_root_dirs = [], - module_path_prefixes = None, - module_roots = None, - node_modules_root = None, - type_check = True): - """Creates an object representing the TypeScript configuration to run the compiler under Bazel. - - Args: - ctx: the starlark execution context - files: Labels of all TypeScript compiler inputs - srcs: Immediate sources being compiled, as opposed to transitive deps. - devmode_manifest: path to the manifest file to write for --target=es5 - tsickle_externs: path to write tsickle-generated externs.js. - type_blocklisted_declarations: types declared in these files will never be - mentioned in generated .d.ts. - out_dir: directory for generated output. Default is ctx.bin_dir - disable_strict_deps: whether to disable the strict deps check - allowed_deps: the set of files that code in srcs may depend on (strict deps) - extra_root_dirs: Extra root dirs to be passed to tsc_wrapped. - module_path_prefixes: additional locations to resolve modules - module_roots: standard locations to resolve modules - node_modules_root: the node_modules root path - - Returns: - A nested dict that corresponds to a tsconfig.json structure - """ - if (type(files) != type([])): - fail("expected files argument to be a list, got " + type(files)) - - outdir_path = out_dir if out_dir != None else ctx.configuration.bin_dir.path - - # Callers can choose the filename for the tsconfig, but it must always live - # in the output directory corresponding with the label where it's declared. - tsconfig_dir = "/".join([ - p - for p in [ - ctx.bin_dir.path, - ctx.label.workspace_root, - ctx.label.package, - ] + ctx.label.name.split("/")[:-1] - # Skip empty path segments (eg. workspace_root when in same repo) - if p - ]) - workspace_path = "/".join([".."] * len(tsconfig_dir.split("/"))) - if module_path_prefixes == None: - module_path_prefixes = [ - "", - ctx.configuration.genfiles_dir.path + "/", - ctx.configuration.bin_dir.path + "/", - ] - if module_roots == None: - base_path_mappings = ["%s/*" % p for p in [ - ".", - ctx.configuration.genfiles_dir.path, - ctx.configuration.bin_dir.path, - ]] - - node_modules_mappings = [] - - # "node_modules" still checked for backward compat for ng_module - if hasattr(ctx.attr, "_typescript_typings") or hasattr(ctx.attr, "node_modules"): - node_modules_mappings.append("/".join([p for p in [ - node_modules_root, - "*", - ] if p])) - - # TypeScript needs to look up ambient types from a 'node_modules' - # directory, but when Bazel manages the dependencies, this directory - # isn't in the project so TypeScript won't find it. - # We can add it to the path mapping to make this lookup work. - # See https://github.com/bazelbuild/rules_typescript/issues/179 - node_modules_mappings.append("/".join([p for p in [ - node_modules_root, - "@types", - "*", - ] if p])) - - module_roots = { - "*": node_modules_mappings, - ctx.workspace_name + "/*": base_path_mappings, - } - module_mappings = get_module_mappings(ctx.label, ctx.attr, srcs = srcs) - - # To determine the path for auto-imports, TypeScript's language service - # considers paths in the order they appear in tsconfig.json. - # We want explicit module mappings ("@angular/core") to take precedence over - # the general "*" mapping (which would create "third_party/javascript/..."), - # so we create a new hash that contains the module_mappings and insert the - # default lookup locations at the end. - mapped_module_roots = {} - for name, path in module_mappings.items(): - # Each module name maps to the immediate path, to resolve "index(.d).ts", - # or module mappings that directly point to files (like index.d.ts). - mapped_module_roots[name] = [ - "%s%s" % (p, path.replace(".d.ts", "")) - for p in module_path_prefixes - ] - if not path.endswith(".d.ts"): - # If not just mapping to a single .d.ts file, include a path glob that - # maps the entire module root. - mapped_module_roots["{}/*".format(name)] = [ - "%s%s/*" % (p, path) - for p in module_path_prefixes - ] - for name, path in module_roots.items(): - mapped_module_roots[name] = path - - # Options for running the TypeScript compiler under Bazel. - # See javascript/typescript/compiler/tsc_wrapped.ts:BazelOptions. - # Unlike compiler_options, the paths here are relative to the rootDir, - # not the location of the tsconfig.json file. - # @unsorted-dict-items preserve historical order for golden tests - bazel_options = { - "workspaceName": ctx.workspace_name, - "target": str(ctx.label), - "package": ctx.label.package, - "tsickleGenerateExterns": getattr(ctx.attr, "generate_externs", True), - "tsickleExternsPath": tsickle_externs.path if tsickle_externs else "", - "untyped": not getattr(ctx.attr, "tsickle_typed", False), - "typeBlackListPaths": [f.path for f in type_blocklisted_declarations], - # This is overridden by first-party javascript/typescript/tsconfig.bzl - "ignoreWarningPaths": [], - "es5Mode": devmode_manifest != None, - "manifest": devmode_manifest if devmode_manifest else "", - # Explicitly tell the compiler which sources we're interested in (emitting - # and type checking). - "compilationTargetSrc": [s.path for s in srcs], - "addDtsClutzAliases": getattr(ctx.attr, "add_dts_clutz_aliases", False), - "typeCheckDependencies": getattr(ctx.attr, "internal_testing_type_check_dependencies", False), - "expectedDiagnostics": getattr(ctx.attr, "expected_diagnostics", []), - "typeCheck": True, - } - - if getattr(ctx.attr, "use_angular_plugin", False): - # @unsorted-dict-items - bazel_options["angularCompilerOptions"] = { - # Needed for back-compat with explicit AOT bootstrap - # which has imports from generated .ngfactory files - "generateNgFactoryShims": True, - # Needed for back-compat with AOT tests which import the - # .ngsummary files - "generateNgSummaryShims": True, - # Bazel expects output files will always be produced - "allowEmptyCodegenFiles": True, - "assets": [a.path for a in getattr(ctx.files, "angular_assets", [])], - } - - if disable_strict_deps: - bazel_options["disableStrictDeps"] = disable_strict_deps - bazel_options["allowedStrictDeps"] = [] - else: - bazel_options["allowedStrictDeps"] = [f.path for f in allowed_deps.to_list()] - - if hasattr(ctx.attr, "module_name") and ctx.attr.module_name: - bazel_options["moduleName"] = ctx.attr.module_name - if hasattr(ctx.attr, "module_root") and ctx.attr.module_root: - bazel_options["moduleRoot"] = ctx.attr.module_root - - if "TYPESCRIPT_WORKER_CACHE_SIZE_MB" in ctx.var: - max_cache_size_mb = int(ctx.var["TYPESCRIPT_WORKER_CACHE_SIZE_MB"]) - if max_cache_size_mb < 0: - fail("TYPESCRIPT_WORKER_CACHE_SIZE_MB set to a negative value (%d)." % max_cache_size_mb) - bazel_options["maxCacheSizeMb"] = max_cache_size_mb - - has_node_runtime = getattr(ctx.attr, "runtime", "browser") == "nodejs" - target_language_level = "es5" if devmode_manifest or has_node_runtime else "es2015" - - # Keep these options in sync with those in playground/playground.ts. - # @unsorted-dict-items preserve historical order for golden tests - compiler_options = { - # De-sugar to this language level - "target": target_language_level, - - # The "typescript.es5_sources" provider is expected to work - # in both nodejs and in browsers, so we use umd in devmode. - # NOTE: tsc-wrapped will always name the enclosed AMD modules - # For production mode, we leave the module syntax alone and let the - # bundler handle it (including dynamic import). - # Note, in google3 we override this option with "commonjs" since Tsickle - # will convert that to goog.module syntax. - "module": "umd" if devmode_manifest or has_node_runtime else "esnext", - - # Has no effect in closure/ES2015 mode. Always true just for simplicity. - "downlevelIteration": True, - - # Do not type-check the lib.*.d.ts. - # We think this shouldn't be necessary but haven't figured out why yet - # and builds are faster with the setting on. - # See http://b/30709121 - "skipDefaultLibCheck": True, - "moduleResolution": "node", - "outDir": "/".join([workspace_path, outdir_path]), - - # We must set a rootDir to avoid TypeScript emit paths varying - # due computeCommonSourceDirectory behavior. - # TypeScript requires the rootDir be a parent of all sources in - # files[], so it must be set to the workspace_path. - "rootDir": workspace_path, - - # Path handling for resolving modules, see specification at - # https://github.com/Microsoft/TypeScript/issues/5039 - # Paths where we attempt to load relative references. - # Longest match wins - # - # tsc_wrapped also uses this property to strip leading paths - # to produce a flattened output tree, see - # https://github.com/Microsoft/TypeScript/issues/8245 - "rootDirs": ["/".join([workspace_path, e]) for e in extra_root_dirs] + [ - workspace_path, - "/".join([workspace_path, ctx.configuration.genfiles_dir.path]), - "/".join([workspace_path, ctx.configuration.bin_dir.path]), - ], - - # Root for non-relative module names - "baseUrl": workspace_path, - - # "short name" mappings for npm packages, such as "@angular/core" - "paths": mapped_module_roots, - - # Inline const enums. - "preserveConstEnums": False, - - # permit `@Decorator` syntax and allow runtime reflection on their types. - "experimentalDecorators": True, - "emitDecoratorMetadata": True, - - # Interpret JSX as React calls (until someone asks for something different) - "jsx": "react", - - # Truncate excessively long errors. - # While truncation can make some errors harder to understand, it makes - # others easier to read. Additionally, for certain errors, TypeScript - # can run out of memory trying to convert them into a humand readable - # string (see https://github.com/Microsoft/TypeScript/issues/37230). - # That's a bug, but the general default configuration of TypeScript is - # to truncate, so following that seems safer and more in line with the - # expected developer UX. - "noErrorTruncation": False, - # Do not emit files if they had errors (avoid accidentally serving - # broken code). - "noEmitOnError": False, - # Create .d.ts files as part of compilation. - "declaration": True, - - # We don't support this compiler option (See github #32), so - # always emit declaration files in the same location as outDir. - "declarationDir": "/".join([workspace_path, outdir_path]), - "stripInternal": True, - - # Embed source maps and sources in .js outputs - "inlineSourceMap": True, - "inlineSources": True, - # Implied by inlineSourceMap: True - "sourceMap": False, - } - - # "node_modules" still checked for backward compat for ng_module - if hasattr(ctx.attr, "_typescript_typings") or hasattr(ctx.attr, "node_modules"): - compiler_options["typeRoots"] = ["/".join([p for p in [ - workspace_path, - node_modules_root, - "@types", - ] if p])] - - if _DEBUG: - compiler_options["traceResolution"] = True - compiler_options["diagnostics"] = True - - # @unsorted-dict-items preserve historical order for golden tests - return { - "compilerOptions": compiler_options, - "bazelOptions": bazel_options, - "files": [workspace_path + "/" + f.path for f in files], - "compileOnSave": False, - }
diff --git a/packages/concatjs/internal/ts_config.bzl b/packages/concatjs/internal/ts_config.bzl deleted file mode 100644 index dcfd52a..0000000 --- a/packages/concatjs/internal/ts_config.bzl +++ /dev/null
@@ -1,57 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"tsconfig.json files using extends" - -TsConfigInfo = provider( - doc = """Provides TypeScript configuration, in the form of a tsconfig.json file - along with any transitively referenced tsconfig.json files chained by the - "extends" feature""", - fields = { - "deps": "all tsconfig.json files needed to configure TypeScript", - }, -) - -def _ts_config_impl(ctx): - files = depset([ctx.file.src]) - transitive_deps = [] - for dep in ctx.attr.deps: - if TsConfigInfo in dep: - transitive_deps.extend(dep[TsConfigInfo].deps) - return [ - DefaultInfo(files = files), - TsConfigInfo(deps = [ctx.file.src] + ctx.files.deps + transitive_deps), - ] - -ts_config = rule( - implementation = _ts_config_impl, - attrs = { - "deps": attr.label_list( - doc = """Additional tsconfig.json files referenced via extends""", - allow_files = True, - ), - "src": attr.label( - doc = """The tsconfig.json file passed to the TypeScript compiler""", - allow_single_file = True, - mandatory = True, - ), - }, - doc = """Allows a tsconfig.json file to extend another file. - -Normally, you just give a single `tsconfig.json` file as the tsconfig attribute -of a `ts_library` rule. However, if your `tsconfig.json` uses the `extends` -feature from TypeScript, then the Bazel implementation needs to know about that -extended configuration file as well, to pass them both to the TypeScript compiler. -""", -)
diff --git a/packages/concatjs/internal/ts_repositories.bzl b/packages/concatjs/internal/ts_repositories.bzl deleted file mode 100644 index a14f8a0..0000000 --- a/packages/concatjs/internal/ts_repositories.bzl +++ /dev/null
@@ -1,64 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"Install toolchain dependencies" - -# BEGIN-DEV-ONLY -# Parts of this BUILD file only necessary when building within the bazelbuild/rules_typescript repo. -# The generated `@bazel/typescript` npm package contains a trimmed BUILD file using # DEV-ONLY fences. -load("@bazel_gazelle//:deps.bzl", "go_repository") - -# END-DEV-ONLY -load("@bazel_skylib//lib:versions.bzl", "versions") - -def ts_setup_workspace(): - """This repository rule should be called from your WORKSPACE file. - - It creates some additional Bazel external repositories that are used internally - by the TypeScript rules. - """ - - # 0.18.0: support for .bazelignore - versions.check("0.18.0") - -# BEGIN-DEV-ONLY -def ts_setup_dev_workspace(): - """ - Setup the toolchain needed for local development, but not needed by users. - - These needs to be in a separate file from ts_setup_workspace() so as not - to leak load statements. - """ - - ts_setup_workspace() - - go_repository( - name = "com_github_google_go_cmp", - commit = "f144a35ed4ac538fae93fa3783175108738f71b9", # v0.5.8 - importpath = "github.com/google/go-cmp", - ) - - go_repository( - name = "com_github_kylelemons_godebug", - commit = "9ff306d4fbead574800b66369df5b6144732d58e", # v1.1.0 - importpath = "github.com/kylelemons/godebug", - ) - - go_repository( - name = "com_github_mattn_go_isatty", - commit = "504425e14f742f1f517c4586048b49b37f829c8e", # v0.0.14 - importpath = "github.com/mattn/go-isatty", - ) - -# END-DEV-ONLY
diff --git a/packages/concatjs/internal/tsc_wrapped/angular_plugin.ts b/packages/concatjs/internal/tsc_wrapped/angular_plugin.ts deleted file mode 100644 index 522e72c..0000000 --- a/packages/concatjs/internal/tsc_wrapped/angular_plugin.ts +++ /dev/null
@@ -1,40 +0,0 @@ -// The `@angular/compiler-cli` module is optional so we only -// access as type-only at the file top-level. -import type {NgTscPlugin} from '@angular/compiler-cli'; - -type CompilerCliModule = typeof import('@angular/compiler-cli'); -type CompilerInteropExports = Partial<CompilerCliModule> & {default?: CompilerCliModule}; - - -/** - * Gets the constructor for instantiating the Angular `ngtsc` - * emit plugin supported by `tsc_wrapped`. - * @throws An error when the Angular emit plugin could not be retrieved. - */ -export async function getAngularEmitPluginOrThrow(): Promise<typeof NgTscPlugin> { - // Note: This is an interop allowing for the `@angular/compiler-cli` package - // to be shipped as strict ESM, or as CommonJS. If the CLI is a CommonJS - // package (pre v13 of Angular), then the exports are in the `default` property. - // See: https://nodejs.org/api/esm.html#esm_import_statements. - // Note: TypeScript downlevels the dynamic `import` to a `require` that is - // not compatible with ESM. We create a function to workaround this issue. - const exports = await loadEsmOrFallbackToRequire<CompilerInteropExports>( - '@angular/compiler-cli'); - const plugin = exports.NgTscPlugin ?? exports.default?.NgTscPlugin; - - if (plugin === undefined) { - throw new Error('Could not find `NgTscPlugin` export in `@angular/compiler-cli`.'); - } - - return plugin; -} - -async function loadEsmOrFallbackToRequire<T>(moduleName: string): Promise<T> { - try { - return await new Function('m', `return import(m);`)(moduleName); - } catch { - // If the dynamic import failed, we still re-try with `require` because - // some NodeJS versions do not even support the dynamic import expression. - return require(moduleName); - } -} \ No newline at end of file
diff --git a/packages/concatjs/internal/tsc_wrapped/cache.ts b/packages/concatjs/internal/tsc_wrapped/cache.ts deleted file mode 100644 index 7fa5f3e..0000000 --- a/packages/concatjs/internal/tsc_wrapped/cache.ts +++ /dev/null
@@ -1,395 +0,0 @@ -/** - * @license - * Copyright 2017 The Bazel Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as fs from 'fs'; -import * as ts from 'typescript'; -import * as perfTrace from './perf_trace'; - -type Debug = (...msg: Array<{}>) => void; - -interface CacheStats { - reads: number; - hits: number; - evictions: number; -} - -/** - * Cache exposes a trivial LRU cache. - * - * This code uses the fact that JavaScript hash maps are linked lists - after - * reaching the cache size limit, it deletes the oldest (first) entries. Used - * cache entries are moved to the end of the list by deleting and re-inserting. - */ -class Cache<T> { - private map = new Map<string, T>(); - private stats: CacheStats = {reads: 0, hits: 0, evictions: 0}; - - constructor(private name: string, private debug: Debug) {} - - set(key: string, value: T) { - this.map.set(key, value); - } - - get(key: string, updateCache = true): T|undefined { - this.stats.reads++; - - const entry = this.map.get(key); - if (updateCache) { - if (entry) { - this.debug(this.name, 'cache hit:', key); - this.stats.hits++; - // Move an entry to the end of the cache by deleting and re-inserting - // it. - this.map.delete(key); - this.map.set(key, entry); - } else { - this.debug(this.name, 'cache miss:', key); - } - this.traceStats(); - } - return entry; - } - - delete(key: string) { - this.map.delete(key); - } - - evict(unevictableKeys?: {has: (key: string) => boolean}): number { - // Drop half the cache, the least recently used entry == the first entry. - this.debug('Evicting from the', this.name, 'cache...'); - const originalSize = this.map.size; - let numberKeysToDrop = originalSize / 2; - if (numberKeysToDrop === 0) { - return 0; - } - // Map keys are iterated in insertion order, since we reinsert on access - // this is indeed a LRU strategy. - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys - for (const key of this.map.keys()) { - if (numberKeysToDrop === 0) break; - if (unevictableKeys && unevictableKeys.has(key)) continue; - this.map.delete(key); - numberKeysToDrop--; - } - const keysDropped = originalSize - this.map.size; - this.stats.evictions += keysDropped; - this.debug('Evicted', keysDropped, this.name, 'cache entries'); - this.traceStats(); - return keysDropped; - } - - keys() { - return this.map.keys(); - } - - resetStats() { - this.stats = {hits: 0, reads: 0, evictions: 0}; - } - - printStats() { - let percentage; - if (this.stats.reads === 0) { - percentage = 100.00; // avoid "NaN %" - } else { - percentage = (this.stats.hits / this.stats.reads * 100).toFixed(2); - } - this.debug(`${this.name} cache stats: ${percentage}% hits`, this.stats); - } - - traceStats() { - // counters are rendered as stacked bar charts, so record cache - // hits/misses rather than the 'reads' stat tracked in stats - // so the chart makes sense. - perfTrace.counter(`${this.name} cache hit rate`, { - 'hits': this.stats.hits, - 'misses': this.stats.reads - this.stats.hits, - }); - perfTrace.counter(`${this.name} cache evictions`, { - 'evictions': this.stats.evictions, - }); - perfTrace.counter(`${this.name} cache size`, { - [`${this.name}s`]: this.map.size, - }); - } -} - -export interface SourceFileEntry { - digest: string; // blaze's opaque digest of the file - value: ts.SourceFile; -} - -/** - * Default memory size, beyond which we evict from the cache. - */ -const DEFAULT_MAX_MEM_USAGE = 1024 * (1 << 20 /* 1 MB */); - -/** - * FileCache is a trivial LRU cache for typescript-parsed bazel-output files. - * - * Cache entries include an opaque bazel-supplied digest to track staleness. - * Expected digests must be set (using updateCache) before using the cache. - */ -// TODO(martinprobst): Drop the <T> parameter, it's no longer used. -export class FileCache<T = {}> { - private fileCache = new Cache<SourceFileEntry>('file', this.debug); - /** - * FileCache does not know how to construct bazel's opaque digests. This - * field caches the last (or current) compile run's digests, so that code - * below knows what digest to assign to a newly loaded file. - */ - private lastDigests = new Map<string, string>(); - /** - * FileCache can enter a degenerate state, where all cache entries are pinned - * by lastDigests, but the system is still out of memory. In that case, do not - * attempt to free memory until lastDigests has changed. - */ - private cannotEvict = false; - - /** - * Because we cannot measuse the cache memory footprint directly, we evict - * when the process' total memory usage goes beyond this number. - */ - private maxMemoryUsage = DEFAULT_MAX_MEM_USAGE; - - constructor(protected debug: (...msg: Array<{}>) => void) {} - - setMaxCacheSize(maxCacheSize: number) { - if (maxCacheSize < 0) { - throw new Error(`FileCache max size is negative: ${maxCacheSize}`); - } - this.debug('Cache max size is', maxCacheSize >> 20, 'MB'); - this.maxMemoryUsage = maxCacheSize; - this.maybeFreeMemory(); - } - - resetMaxCacheSize() { - this.setMaxCacheSize(DEFAULT_MAX_MEM_USAGE); - } - - /** - * Updates the cache with the given digests. - * - * updateCache must be called before loading files - only files that were - * updated (with a digest) previously can be loaded. - */ - updateCache(digests: Map<string, string>): void { - this.debug('updating digests:', digests); - this.lastDigests = digests; - this.cannotEvict = false; - for (const [filePath, newDigest] of digests.entries()) { - const entry = this.fileCache.get(filePath, /*updateCache=*/ false); - if (entry && entry.digest !== newDigest) { - this.debug( - 'dropping file cache entry for', filePath, 'digests', entry.digest, - newDigest); - this.fileCache.delete(filePath); - } - } - } - - getLastDigest(filePath: string): string { - const digest = this.lastDigests.get(filePath); - if (!digest) { - const errorMsg = `missing input digest for ${filePath}. `; - let entriesToPrint = Array.from(this.lastDigests.keys()); - if (entriesToPrint.length > 100) { - throw new Error( - errorMsg + - `(only have ${entriesToPrint.slice(0, 100)} and ${ - entriesToPrint.length - 100} more)`); - } - throw new Error(errorMsg + `(only have ${entriesToPrint})`); - } - return digest; - } - - getCache(filePath: string): ts.SourceFile|undefined { - const entry = this.fileCache.get(filePath); - if (entry) return entry.value; - return undefined; - } - - putCache(filePath: string, entry: SourceFileEntry): void { - const dropped = this.maybeFreeMemory(); - this.fileCache.set(filePath, entry); - this.debug('Loaded file:', filePath, 'dropped', dropped, 'files'); - } - - /** - * Returns true if the given filePath was reported as an input up front and - * has a known cache digest. FileCache can only cache known files. - */ - isKnownInput(filePath: string): boolean { - return this.lastDigests.has(filePath); - } - - inCache(filePath: string): boolean { - return !!this.getCache(filePath); - } - - resetStats() { - this.fileCache.resetStats(); - } - - printStats() { - this.fileCache.printStats(); - } - - traceStats() { - this.fileCache.traceStats(); - } - - /** - * Returns whether the cache should free some memory. - * - * Defined as a property so it can be overridden in tests. - */ - shouldFreeMemory: () => boolean = () => { - return process.memoryUsage().heapUsed > this.maxMemoryUsage; - }; - - /** - * Frees memory if required. Returns the number of dropped entries. - */ - maybeFreeMemory() { - if (!this.shouldFreeMemory() || this.cannotEvict) { - return 0; - } - const dropped = this.fileCache.evict(this.lastDigests); - if (dropped === 0) { - // Freeing memory did not drop any cache entries, because all are pinned. - // Stop evicting until the pinned list changes again. This prevents - // degenerating into an O(n^2) situation where each file load iterates - // through the list of all files, trying to evict cache keys in vain - // because all are pinned. - this.cannotEvict = true; - } - return dropped; - } - - getFileCacheKeysForTest() { - return Array.from(this.fileCache.keys()); - } -} - -/** - * ProgramAndFileCache is a trivial LRU cache for typescript-parsed programs and - * bazel-output files. - * - * Programs are evicted before source files because they have less reuse across - * compilations. - */ -export class ProgramAndFileCache extends FileCache { - private programCache = new Cache<ts.Program>('program', this.debug); - - getProgram(target: string): ts.Program|undefined { - return this.programCache.get(target); - } - - putProgram(target: string, program: ts.Program): void { - const dropped = this.maybeFreeMemory(); - this.programCache.set(target, program); - this.debug('Loaded program:', target, 'dropped', dropped, 'entries'); - } - - resetStats() { - super.resetStats(); - this.programCache.resetStats(); - } - - printStats() { - super.printStats(); - this.programCache.printStats(); - } - - traceStats() { - super.traceStats(); - this.programCache.traceStats(); - } - - maybeFreeMemory() { - if (!this.shouldFreeMemory()) return 0; - - const dropped = this.programCache.evict(); - if (dropped > 0) return dropped; - - return super.maybeFreeMemory(); - } - - getProgramCacheKeysForTest() { - return Array.from(this.programCache.keys()); - } -} - -export interface FileLoader { - loadFile(fileName: string, filePath: string, langVer: ts.ScriptTarget): - ts.SourceFile; - fileExists(filePath: string): boolean; -} - -/** - * Load a source file from disk, or possibly return a cached version. - */ -export class CachedFileLoader implements FileLoader { - /** Total amount of time spent loading files, for the perf trace. */ - private totalReadTimeMs = 0; - - // TODO(alexeagle): remove unused param after usages updated: - // angular:packages/bazel/src/ngc-wrapped/index.ts - constructor(private readonly cache: FileCache, unused?: boolean) {} - - fileExists(filePath: string) { - return this.cache.isKnownInput(filePath); - } - - loadFile(fileName: string, filePath: string, langVer: ts.ScriptTarget): - ts.SourceFile { - let sourceFile = this.cache.getCache(filePath); - if (!sourceFile) { - const readStart = Date.now(); - const sourceText = fs.readFileSync(filePath, 'utf8'); - sourceFile = ts.createSourceFile(fileName, sourceText, langVer, true); - const entry = { - digest: this.cache.getLastDigest(filePath), - value: sourceFile - }; - const readEnd = Date.now(); - this.cache.putCache(filePath, entry); - - this.totalReadTimeMs += readEnd - readStart; - perfTrace.counter('file load time', { - 'read': this.totalReadTimeMs, - }); - perfTrace.snapshotMemoryUsage(); - } - - return sourceFile; - } -} - -/** Load a source file from disk. */ -export class UncachedFileLoader implements FileLoader { - fileExists(filePath: string): boolean { - return ts.sys.fileExists(filePath); - } - - loadFile(fileName: string, filePath: string, langVer: ts.ScriptTarget): - ts.SourceFile { - const sourceText = fs.readFileSync(filePath, 'utf8'); - return ts.createSourceFile(fileName, sourceText, langVer, true); - } -}
diff --git a/packages/concatjs/internal/tsc_wrapped/cache_test.ts b/packages/concatjs/internal/tsc_wrapped/cache_test.ts deleted file mode 100644 index 09e0afa..0000000 --- a/packages/concatjs/internal/tsc_wrapped/cache_test.ts +++ /dev/null
@@ -1,244 +0,0 @@ -/** - * @license - * Copyright 2017 The Bazel Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'jasmine'; - -import * as ts from 'typescript'; - -import {CachedFileLoader, FileCache, ProgramAndFileCache} from './cache'; -import {invalidateFileCache, writeTempFile} from './test_support'; - -function fauxDebug(...args: [any?, ...any[]]) { - console.error.apply(console, args); -} - -describe('Cache', () => { - let fileCache: FileCache; - let fileLoader: CachedFileLoader; - - function loadFile(name: string, fn: string) { - return fileLoader.loadFile(name, fn, ts.ScriptTarget.ES5); - } - - function setCurrentFiles(...fileName: string[]) { - // Give all files the same digest, so that the file cache allows reading - // them, but does not evict them. - const digests = - new Map(fileName.map((fn): [string, string] => [fn, 'digest'])); - fileCache.updateCache(digests); - } - - function expectFileCacheKeys() { - // Strip the long /tmp paths created by writeTempFile. - return expect( - fileCache.getFileCacheKeysForTest().map(fn => fn.replace(/.*\./, ''))); - } - - describe('FileCache', () => { - beforeEach(() => { - fileCache = new FileCache(fauxDebug); - fileLoader = new CachedFileLoader(fileCache); - }); - - it('caches files', () => { - const fn = writeTempFile('file_cache_test', 'let x: number = 12;\n'); - invalidateFileCache(fileCache, fn); - - // Caches. - const sourceFile = - fileLoader.loadFile('fileName', fn, ts.ScriptTarget.ES5); - let sourceFile2 = - fileLoader.loadFile('fileName', fn, ts.ScriptTarget.ES5); - expect(sourceFile).toBe(sourceFile2); // i.e., identical w/ === - - // Invalidate the file. - invalidateFileCache(fileCache, fn); - sourceFile2 = fileLoader.loadFile('fileName', fn, ts.ScriptTarget.ES5); - // New file after write/mtime change. - expect(sourceFile).not.toBe(sourceFile2); - }); - - it('caches in LRU order', () => { - let free = false; - fileCache.shouldFreeMemory = () => free; - - const fn1 = writeTempFile('file_cache_test1', 'let x: number = 1;\n'); - const fn2 = writeTempFile('file_cache_test2', 'let x: number = 2;\n'); - const fn3 = writeTempFile('file_cache_test3', 'let x: number = 3;\n'); - const fn4 = writeTempFile('file_cache_test4', 'let x: number = 4;\n'); - const fn5 = writeTempFile('file_cache_test5', 'let x: number = 5;\n'); - setCurrentFiles(fn1, fn2, fn3, fn4, fn5); - - // Populate the cache. - const f1 = loadFile('f1', fn1); - const f2 = loadFile('f2', fn2); - const f3 = loadFile('f3', fn3); - const f4 = loadFile('f4', fn4); - - expectFileCacheKeys().toEqual([ - 'file_cache_test1', - 'file_cache_test2', - 'file_cache_test3', - 'file_cache_test4', - ]); - - // Load f1 from cache again. Now f1 is the most recently used file. - expect(loadFile('f1', fn1)).toBe(f1, 'f1 should be cached'); - expectFileCacheKeys().toEqual([ - 'file_cache_test2', - 'file_cache_test3', - 'file_cache_test4', - 'file_cache_test1', - ]); - - // Now load f5 and pretend memory must be freed. - // length / 2 == 2 files must be cleared. - // f2 + f5 are pinned because they are part of the compilation unit. - // f1, f3, f4 are eligible for eviction, and f1 is more recently used than - // the others, so f1 shoud be retained, f3 + f4 dropped. - - setCurrentFiles(fn2, fn5); - free = true; - const f5 = loadFile('f5', fn5); - expectFileCacheKeys().toEqual([ - 'file_cache_test2', - 'file_cache_test1', - 'file_cache_test5', - ]); - setCurrentFiles(fn1, fn2, fn3, fn4, fn5); - expect(loadFile('f1', fn1)) - .toBe(f1, 'f1 should not be dropped, it was recently used'); - expect(loadFile('f2', fn2)).toBe(f2, 'f2 should be pinned'); - expect(loadFile('f3', fn3)).not.toBe(f3, 'f3 should have been dropped'); - expect(loadFile('f4', fn4)).not.toBe(f4, 'f4 should have been dropped'); - expect(loadFile('f5', fn5)).toBe(f5, 'f5 should be pinned'); - }); - - it('degenerates to cannotEvict mode', () => { - // Pretend to always be out of memory. - fileCache.shouldFreeMemory = () => true; - - const fn1 = writeTempFile('file_cache_test1', 'let x: number = 1;\n'); - const fn2 = writeTempFile('file_cache_test2', 'let x: number = 2;\n'); - const fn3 = writeTempFile('file_cache_test3', 'let x: number = 3;\n'); - const fn4 = writeTempFile('file_cache_test4', 'let x: number = 4;\n'); - setCurrentFiles(fn1, fn2, fn3); - - loadFile('fn1', fn1), loadFile('fn2', fn2); - loadFile('fn3', fn3); - - expect(fileCache['cannotEvict']).toBe(true, 'all files are pinned'); - expectFileCacheKeys().toEqual([ - 'file_cache_test1', - 'file_cache_test2', - 'file_cache_test3', - ]); - setCurrentFiles(fn1, fn4); - expect(fileCache['cannotEvict']).toBe(false, 'pinned files reset'); - loadFile('fn4', fn4); - expectFileCacheKeys().toEqual([ - 'file_cache_test1', - 'file_cache_test4', - ]); - }); - }); - - describe('ProgramAndFileCache', () => { - let cache: ProgramAndFileCache; - - function loadProgram(name: string): ts.Program { - const p = {} as ts.Program; - cache.putProgram(name, p); - return p; - } - - function expectProgramCacheKeys() { - return expect(cache.getProgramCacheKeysForTest()); - } - - beforeEach(() => { - fileCache = new ProgramAndFileCache(fauxDebug); - fileLoader = new CachedFileLoader(fileCache); - cache = fileCache as ProgramAndFileCache; - }); - - it('caches programs', () => { - const name = 'fauxprogram'; - const program = loadProgram(name); - - expect(cache.getProgram(name)).toBe(program); - }); - - it('caches programs in LRU order', () => { - let free = false; - cache.shouldFreeMemory = () => free; - - // Populate the cache. - const p1 = loadProgram('p1'); - loadProgram('p2'); - loadProgram('p3'); - loadProgram('p4'); - - expectProgramCacheKeys().toEqual([ - 'p1', - 'p2', - 'p3', - 'p4', - ]); - - // Load p1 from cache again. Now p1 is the most recently used program. - expect(cache.getProgram('p1')).toBe(p1); - expectProgramCacheKeys().toEqual([ - 'p2', - 'p3', - 'p4', - 'p1', - ]); - - // Now load p5 and pretend memory must be freed. - // length / 2 == 2 files must be cleared. - - free = true; - loadProgram('p5'); - expectProgramCacheKeys().toEqual([ - 'p4', - 'p1', - 'p5', - ]); - }); - - it('evicts programs before files', () => { - let free = false; - cache.shouldFreeMemory = () => free; - - // Populate the cache. - loadProgram('p1'); - const fn1 = writeTempFile('file_cache_test1', 'let x: number = 1;\n'); - const fn2 = writeTempFile('file_cache_test2', 'let x: number = 2;\n'); - setCurrentFiles(fn1); - loadFile('fn1', fn1); - expectProgramCacheKeys().toEqual(['p1']); - expectFileCacheKeys().toEqual(['file_cache_test1']); - - free = true; - setCurrentFiles(fn2); - loadFile('fn2', fn2); - expectProgramCacheKeys().toEqual([]); - expectFileCacheKeys().toEqual(['file_cache_test1', 'file_cache_test2']); - }); - }); -});
diff --git a/packages/concatjs/internal/tsc_wrapped/compiler_host.ts b/packages/concatjs/internal/tsc_wrapped/compiler_host.ts deleted file mode 100644 index 0336ec4..0000000 --- a/packages/concatjs/internal/tsc_wrapped/compiler_host.ts +++ /dev/null
@@ -1,653 +0,0 @@ -import * as fs from 'fs'; -import * as path from 'path'; -import * as tsickle from 'tsickle'; -import * as ts from 'typescript'; - -import {FileLoader} from './cache'; -import * as perfTrace from './perf_trace'; -import {BazelOptions} from './tsconfig'; -import {DEBUG, debug} from './worker'; - -export type ModuleResolver = - (moduleName: string, containingFile: string, - compilerOptions: ts.CompilerOptions, host: ts.ModuleResolutionHost) => - ts.ResolvedModuleWithFailedLookupLocations; - -/** - * Narrows down the type of some properties from non-optional to required, so - * that we do not need to check presence before each access. - */ -export interface BazelTsOptions extends ts.CompilerOptions { - rootDirs: string[]; - rootDir: string; - outDir: string; - typeRoots: string[]; -} - -declare interface packageJson { - typings?: string; -} - -export function narrowTsOptions(options: ts.CompilerOptions): BazelTsOptions { - if (!options.rootDirs) { - throw new Error(`compilerOptions.rootDirs should be set by tsconfig.bzl`); - } - if (!options.rootDir) { - throw new Error(`compilerOptions.rootDir should be set by tsconfig.bzl`); - } - if (!options.outDir) { - throw new Error(`compilerOptions.outDir should be set by tsconfig.bzl`); - } - return options as BazelTsOptions; -} - -function validateBazelOptions(bazelOpts: BazelOptions) { - if (!bazelOpts.isJsTranspilation) return; - - if (bazelOpts.compilationTargetSrc && - bazelOpts.compilationTargetSrc.length > 1) { - throw new Error( - 'In JS transpilation mode, only one file can appear in ' + - 'bazelOptions.compilationTargetSrc.'); - } - - if (!bazelOpts.transpiledJsOutputFileName && - !bazelOpts.transpiledJsOutputDirectory) { - throw new Error( - 'In JS transpilation mode, either transpiledJsOutputFileName or ' + - 'transpiledJsOutputDirectory must be specified in tsconfig.'); - } - - if (bazelOpts.transpiledJsOutputFileName && - bazelOpts.transpiledJsOutputDirectory) { - throw new Error( - 'In JS transpilation mode, cannot set both ' + - 'transpiledJsOutputFileName and transpiledJsOutputDirectory.'); - } -} - -const SOURCE_EXT = /((\.d)?\.tsx?|\.js)$/; - -const MAJOR_TS_VERSION = parseInt(ts.versionMajorMinor.split('.')[0], 10); - -/** - * CompilerHost that knows how to cache parsed files to improve compile times. - */ -export class CompilerHost implements ts.CompilerHost, tsickle.TsickleHost { - /** - * Lookup table to answer file stat's without looking on disk. - */ - private knownFiles = new Set<string>(); - - /** - * rootDirs relative to the rootDir, eg "bazel-out/local-fastbuild/bin" - */ - private relativeRoots: string[]; - - getCancelationToken?: () => ts.CancellationToken; - directoryExists?: (dir: string) => boolean; - - generateExtraSuppressions: boolean; - - googmodule: boolean; - es5Mode: boolean; - prelude: string; - untyped: boolean; - typeBlackListPaths: Set<string>; - transformDecorators: boolean; - transformTypesToClosure: boolean; - addDtsClutzAliases: boolean; - isJsTranspilation: boolean; - provideExternalModuleDtsNamespace: boolean; - options: BazelTsOptions; - moduleResolutionHost: ts.ModuleResolutionHost = this; - // TODO(evanm): delete this once tsickle is updated. - host: ts.ModuleResolutionHost = this; - private allowActionInputReads = true; - - constructor( - public inputFiles: string[], options: ts.CompilerOptions, - readonly bazelOpts: BazelOptions, private delegate: ts.CompilerHost, - private fileLoader: FileLoader, - private moduleResolver: ModuleResolver = ts.resolveModuleName) { - this.options = narrowTsOptions(options); - this.relativeRoots = - this.options.rootDirs.map(r => path.relative(this.options.rootDir, r)); - inputFiles.forEach((f) => { - this.knownFiles.add(f); - }); - - // getCancelationToken is an optional method on the delegate. If we - // unconditionally implement the method, we will be forced to return null, - // in the absense of the delegate method. That won't match the return type. - // Instead, we optionally set a function to a field with the same name. - if (delegate && delegate.getCancellationToken) { - this.getCancelationToken = delegate.getCancellationToken.bind(delegate); - } - - // Override directoryExists so that TypeScript can automatically - // include global typings from node_modules/@types - // see getAutomaticTypeDirectiveNames in - // TypeScript:src/compiler/moduleNameResolver - if (this.allowActionInputReads && delegate && delegate.directoryExists) { - this.directoryExists = delegate.directoryExists.bind(delegate); - } - - this.generateExtraSuppressions = true; - - validateBazelOptions(bazelOpts); - this.googmodule = bazelOpts.googmodule; - this.es5Mode = bazelOpts.es5Mode; - this.prelude = bazelOpts.prelude; - this.untyped = bazelOpts.untyped; - this.typeBlackListPaths = new Set(bazelOpts.typeBlackListPaths); - this.transformDecorators = bazelOpts.tsickle; - this.transformTypesToClosure = bazelOpts.tsickle; - this.addDtsClutzAliases = bazelOpts.addDtsClutzAliases; - this.isJsTranspilation = Boolean(bazelOpts.isJsTranspilation); - this.provideExternalModuleDtsNamespace = !bazelOpts.hasImplementation; - } - - /** - * For the given potentially absolute input file path (typically .ts), returns - * the relative output path. For example, for - * /path/to/root/blaze-out/k8-fastbuild/genfiles/my/file.ts, will return - * my/file.js or my/file.mjs (depending on ES5 mode). - */ - relativeOutputPath(fileName: string) { - let result = this.rootDirsRelative(fileName); - result = result.replace(/(\.d)?\.[jt]sx?$/, ''); - if (!this.bazelOpts.es5Mode) result += '.closure'; - return result + '.js'; - } - - /** - * Workaround https://github.com/Microsoft/TypeScript/issues/8245 - * We use the `rootDirs` property both for module resolution, - * and *also* to flatten the structure of the output directory - * (as `rootDir` would do for a single root). - * To do this, look for the pattern outDir/relativeRoots[i]/path/to/file - * or relativeRoots[i]/path/to/file - * and replace that with path/to/file - */ - flattenOutDir(fileName: string): string { - let result = fileName; - - // outDir/relativeRoots[i]/path/to/file -> relativeRoots[i]/path/to/file - if (fileName.startsWith(this.options.rootDir)) { - result = path.relative(this.options.outDir, fileName); - } - - for (const dir of this.relativeRoots) { - // relativeRoots[i]/path/to/file -> path/to/file - const rel = path.relative(dir, result); - if (!rel.startsWith('..')) { - result = rel; - // relativeRoots is sorted longest first so we can short-circuit - // after the first match - break; - } - } - return result; - } - - /** Avoid using tsickle on files that aren't in srcs[] */ - shouldSkipTsickleProcessing(fileName: string): boolean { - return this.bazelOpts.isJsTranspilation || - this.bazelOpts.compilationTargetSrc.indexOf(fileName) === -1; - } - - /** Whether the file is expected to be imported using a named module */ - shouldNameModule(fileName: string): boolean { - return this.bazelOpts.compilationTargetSrc.indexOf(fileName) !== -1; - } - - /** Allows suppressing warnings for specific known libraries */ - shouldIgnoreWarningsForPath(filePath: string): boolean { - return this.bazelOpts.ignoreWarningPaths.some( - p => !!filePath.match(new RegExp(p))); - } - - /** - * fileNameToModuleId gives the module ID for an input source file name. - * @param fileName an input source file name, e.g. - * /root/dir/bazel-out/host/bin/my/file.ts. - * @return the canonical path of a file within blaze, without /genfiles/ or - * /bin/ path parts, excluding a file extension. For example, "my/file". - */ - fileNameToModuleId(fileName: string): string { - return this.relativeOutputPath( - fileName.substring(0, fileName.lastIndexOf('.'))); - } - - /** - * TypeScript SourceFile's have a path with the rootDirs[i] still present, eg. - * /build/work/bazel-out/local-fastbuild/bin/path/to/file - * @return the path without any rootDirs, eg. path/to/file - */ - rootDirsRelative(fileName: string): string { - for (const root of this.options.rootDirs) { - if (fileName.startsWith(root)) { - // rootDirs are sorted longest-first, so short-circuit the iteration - // see tsconfig.ts. - return path.posix.relative(root, fileName); - } - } - return fileName; - } - - /** - * Massages file names into valid goog.module names: - * - resolves relative paths to the given context - * - resolves non-relative paths which takes module_root into account - * - replaces '/' with '.' in the '<workspace>' namespace - * - replace first char if non-alpha - * - replace subsequent non-alpha numeric chars - */ - pathToModuleName(context: string, importPath: string): string { - // tsickle hands us an output path, we need to map it back to a source - // path in order to do module resolution with it. - // outDir/relativeRoots[i]/path/to/file -> - // rootDir/relativeRoots[i]/path/to/file - if (context.startsWith(this.options.outDir)) { - context = path.join( - this.options.rootDir, path.relative(this.options.outDir, context)); - } - - // Try to get the resolved path name from TS compiler host which can - // handle resolution for libraries with module_root like rxjs and @angular. - let resolvedPath: string|null = null; - const resolved = - this.moduleResolver(importPath, context, this.options, this); - if (resolved && resolved.resolvedModule && - resolved.resolvedModule.resolvedFileName) { - resolvedPath = resolved.resolvedModule.resolvedFileName; - // /build/work/bazel-out/local-fastbuild/bin/path/to/file -> - // path/to/file - resolvedPath = this.rootDirsRelative(resolvedPath); - } else { - // importPath can be an absolute file path in google3. - // Try to trim it as a path relative to bin and genfiles, and if so, - // handle its file extension in the block below and prepend the workspace - // name. - const trimmed = this.rootDirsRelative(importPath); - if (trimmed !== importPath) { - resolvedPath = trimmed; - } - } - if (resolvedPath) { - // Strip file extensions. - importPath = resolvedPath.replace(SOURCE_EXT, ''); - // Make sure all module names include the workspace name. - if (importPath.indexOf(this.bazelOpts.workspaceName) !== 0) { - importPath = path.posix.join(this.bazelOpts.workspaceName, importPath); - } - } - - // Remove the __{LOCALE} from the module name. - if (this.bazelOpts.locale) { - const suffix = '__' + this.bazelOpts.locale.toLowerCase(); - if (importPath.toLowerCase().endsWith(suffix)) { - importPath = importPath.substring(0, importPath.length - suffix.length); - } - } - - // Replace characters not supported by goog.module and '.' with - // '$<Hex char code>' so that the original module name can be re-obtained - // without any loss. - // See goog.VALID_MODULE_RE_ in Closure's base.js for characters supported - // by google.module. - - const escape = (c: string) => { - return '$' + c.charCodeAt(0).toString(16); - }; - const moduleName = importPath.replace(/^[0-9]|[^a-zA-Z_0-9_/]/g, escape) - .replace(/\//g, '.'); - return moduleName; - } - - /** - * Converts file path into a valid AMD module name. - * - * An AMD module can have an arbitrary name, so that it is require'd by name - * rather than by path. See http://requirejs.org/docs/whyamd.html#namedmodules - * - * "However, tools that combine multiple modules together for performance need - * a way to give names to each module in the optimized file. For that, AMD - * allows a string as the first argument to define()" - */ - amdModuleName(sf: ts.SourceFile): string|undefined { - if (!this.shouldNameModule(sf.fileName)) return undefined; - // /build/work/bazel-out/local-fastbuild/bin/path/to/file.ts - // -> path/to/file - let fileName = this.rootDirsRelative(sf.fileName).replace(SOURCE_EXT, ''); - - let workspace = this.bazelOpts.workspaceName; - - // Workaround https://github.com/bazelbuild/bazel/issues/1262 - // - // When the file comes from an external bazel repository, - // and TypeScript resolves runfiles symlinks, then the path will look like - // output_base/execroot/local_repo/external/another_repo/foo/bar - // We want to name such a module "another_repo/foo/bar" just as it would be - // named by code in that repository. - // As a workaround, check for the /external/ path segment, and fix up the - // workspace name to be the name of the external repository. - if (fileName.startsWith('external/')) { - const parts = fileName.split('/'); - workspace = parts[1]; - fileName = parts.slice(2).join('/'); - } - - if (this.bazelOpts.moduleName) { - const relativeFileName = path.posix.relative(this.bazelOpts.package, fileName); - // check that the fileName was actually underneath the package directory - if (!relativeFileName.startsWith('..')) { - if (this.bazelOpts.moduleRoot) { - const root = this.bazelOpts.moduleRoot.replace(SOURCE_EXT, ''); - if (root === relativeFileName || - path.posix.join(root, 'index') === relativeFileName) { - return this.bazelOpts.moduleName; - } - } - // Support the common case of commonjs convention that index is the - // default module in a directory. - // This makes our module naming scheme more conventional and lets users - // refer to modules with the natural name they're used to. - if (relativeFileName === 'index') { - return this.bazelOpts.moduleName; - } - return path.posix.join(this.bazelOpts.moduleName, relativeFileName); - } - } - - if (fileName.startsWith('node_modules/')) { - return fileName.substring('node_modules/'.length); - } - - // path/to/file -> - // myWorkspace/path/to/file - return path.posix.join(workspace, fileName); - } - - /** - * Resolves the typings file from a package at the specified path. Helper - * function to `resolveTypeReferenceDirectives`. - */ - private resolveTypingFromDirectory(typePath: string, primary: boolean): ts.ResolvedTypeReferenceDirective | undefined { - // Looks for the `typings` attribute in a package.json file - // if it exists - const pkgFile = path.posix.join(typePath, 'package.json'); - if (this.fileExists(pkgFile)) { - const pkg = JSON.parse(fs.readFileSync(pkgFile, 'utf-8')) as packageJson; - let typings = pkg['typings']; - if (typings) { - if (typings === '.' || typings === './') { - typings = 'index.d.ts'; - } - const maybe = path.posix.join(typePath, typings); - if (this.fileExists(maybe)) { - return { primary, resolvedFileName: maybe }; - } - } - } - - // Look for an index.d.ts file in the path - const maybe = path.posix.join(typePath, 'index.d.ts'); - if (this.fileExists(maybe)) { - return { primary, resolvedFileName: maybe }; - } - - return undefined; - } - - /** - * Override the default typescript resolveTypeReferenceDirectives function. - * Resolves /// <reference types="x" /> directives under bazel. The default - * typescript secondary search behavior needs to be overridden to support - * looking under `bazelOpts.nodeModulesPrefix` - */ - resolveTypeReferenceDirectives(names: string[] | ts.FileReference[]): (ts.ResolvedTypeReferenceDirective|undefined)[] { - if (!this.allowActionInputReads) return []; - const result: (ts.ResolvedTypeReferenceDirective|undefined)[] = []; - names.forEach(name => { - const fileName = typeof name === 'string' ? name : name.fileName; - let resolved: ts.ResolvedTypeReferenceDirective | undefined; - - // primary search - if (this.options.typeRoots) { - this.options.typeRoots.forEach(typeRoot => { - if (!resolved) { - resolved = this.resolveTypingFromDirectory(path.posix.join(typeRoot, fileName), true); - } - }); - } - - // secondary search - if (!resolved) { - resolved = this.resolveTypingFromDirectory(path.posix.join(this.bazelOpts.nodeModulesPrefix, fileName), false); - } - - // Types not resolved should be silently ignored. Leave it to Typescript - // to either error out with "TS2688: Cannot find type definition file for - // 'foo'" or for the build to fail due to a missing type that is used. - if (!resolved && DEBUG) { - debug(`Failed to resolve type reference directive '${fileName}'`); - } - // In typescript 2.x the return type for this function - // is `(ts.ResolvedTypeReferenceDirective | undefined)[]` thus we actually - // do allow returning `undefined` in the array but the function is typed - // `(ts.ResolvedTypeReferenceDirective)[]` to compile with both typescript - // 2.x and 3.0/3.1 without error. Typescript 3.0/3.1 do handle the `undefined` - // values in the array correctly despite the return signature. - // It looks like the return type change was a mistake because - // it was changed back to include `| undefined` recently: - // https://github.com/Microsoft/TypeScript/pull/28059. - // As of version 5, it appears that the `undefined` values are expected - // to be in the results, or the compiler will throw an error. - if (resolved || MAJOR_TS_VERSION >= 5) { - result.push(resolved); - } - }); - return result; - } - - /** Loads a source file from disk (or the cache). */ - getSourceFile( - fileName: string, languageVersion: ts.ScriptTarget, - onError?: (message: string) => void) { - return perfTrace.wrap(`getSourceFile ${fileName}`, () => { - const sf = this.fileLoader.loadFile(fileName, fileName, languageVersion) as - ts.SourceFile&{_hasGeneratedAmdModuleName?: boolean}; - - if (!/\.d\.tsx?$/.test(fileName) && - (this.options.module === ts.ModuleKind.AMD || - this.options.module === ts.ModuleKind.UMD)) { - const moduleName = this.amdModuleName(sf); - if (sf.moduleName === moduleName || !moduleName) return sf; - if (sf.moduleName) { - throw new Error( - `ERROR: ${sf.fileName} ` + - `contains a module name declaration ${sf.moduleName} ` + - `which would be overwritten with ${moduleName} ` + - `by Bazel's TypeScript compiler.`); - } - // Setting the moduleName is equivalent to the original source having the triple - // slash `///<amd-module name="some/name"/>` directive. Also note that we tag - // source files for which we assigned a generated module name. This is necessary - // so that we can reset the module name when the same source file is loaded from - // a cache, but with a different module format where the auto-generated module - // names are not desirable. The module name should not leak from previous - // compilations through a potential source file cache. - sf._hasGeneratedAmdModuleName = true; - sf.moduleName = moduleName; - return sf; - } - - // If the loaded source file has a generated amd module name applied from - // previous compilations (in worker mode), reset the file module name - // as neither the UMD or AMD module format is used (for which we generate - // the AMD module names automatically). - if (sf._hasGeneratedAmdModuleName) { - sf.moduleName = undefined; - } - - return sf; - }); - } - - writeFile( - fileName: string, content: string, writeByteOrderMark: boolean, - onError: ((message: string) => void)|undefined, - sourceFiles: ReadonlyArray<ts.SourceFile>|undefined): void { - perfTrace.wrap( - `writeFile ${fileName}`, - () => this.writeFileImpl( - fileName, content, writeByteOrderMark, onError, sourceFiles)); - } - - writeFileImpl( - fileName: string, content: string, writeByteOrderMark: boolean, - onError: ((message: string) => void)|undefined, - sourceFiles: ReadonlyArray<ts.SourceFile>|undefined): void { - // Workaround https://github.com/Microsoft/TypeScript/issues/18648 - // This bug is fixed in TS 2.9 - const version = ts.versionMajorMinor; - const [major, minor] = version.split('.').map(s => Number(s)); - const workaroundNeeded = major <= 2 && minor <= 8; - if (workaroundNeeded && - (this.options.module === ts.ModuleKind.AMD || - this.options.module === ts.ModuleKind.UMD) && - fileName.endsWith('.d.ts') && sourceFiles && sourceFiles.length > 0 && - sourceFiles[0].moduleName) { - content = - `/// <amd-module name="${sourceFiles[0].moduleName}" />\n${content}`; - } - fileName = this.flattenOutDir(fileName); - - if (this.bazelOpts.isJsTranspilation) { - if (this.bazelOpts.transpiledJsOutputFileName) { - fileName = this.bazelOpts.transpiledJsOutputFileName!; - } else { - // Strip the input directory path off of fileName to get the logical - // path within the input directory. - fileName = - path.relative(this.bazelOpts.transpiledJsInputDirectory!, fileName); - // Then prepend the output directory name. - fileName = - path.join(this.bazelOpts.transpiledJsOutputDirectory!, fileName); - } - } else if (!this.bazelOpts.es5Mode) { - // Write ES6 transpiled files to *.mjs. - if (this.bazelOpts.locale) { - // i18n paths are required to end with __locale.js so we put - // the .closure segment before the __locale - fileName = fileName.replace(/(__[^\.]+)?\.js$/, '.closure$1.js'); - } else { - fileName = fileName.replace(/\.js$/, '.mjs'); - } - } - - // Prepend the output directory. - fileName = path.join(this.options.outDir, fileName); - - // Our file cache is based on mtime - so avoid writing files if they - // did not change. - if (!fs.existsSync(fileName) || - fs.readFileSync(fileName, 'utf-8') !== content) { - this.delegate.writeFile( - fileName, content, writeByteOrderMark, onError, sourceFiles); - } - } - - /** - * Performance optimization: don't try to stat files we weren't explicitly - * given as inputs. - * This also allows us to disable Bazel sandboxing, without accidentally - * reading .ts inputs when .d.ts inputs are intended. - * Note that in worker mode, the file cache will also guard against arbitrary - * file reads. - */ - fileExists(filePath: string): boolean { - // Under Bazel, users do not declare deps[] on their node_modules. - // This means that we do not list all the needed .d.ts files in the files[] - // section of tsconfig.json, and that is what populates the knownFiles set. - // In addition, the node module resolver may need to read package.json files - // and these are not permitted in the files[] section. - // So we permit reading node_modules/* from action inputs, even though this - // can include data[] dependencies and is broader than we would like. - // This should only be enabled under Bazel, not Blaze. - if (this.allowActionInputReads && filePath.indexOf('/node_modules/') >= 0) { - const result = this.fileLoader.fileExists(filePath); - if (DEBUG && !result && this.delegate.fileExists(filePath)) { - debug("Path exists, but is not registered in the cache", filePath); - Object.keys((this.fileLoader as any).cache.lastDigests).forEach(k => { - if (k.endsWith(path.basename(filePath))) { - debug(" Maybe you meant to load from", k); - } - }); - } - return result; - } - return this.knownFiles.has(filePath); - } - - getDefaultLibLocation(): string { - // Since we override getDefaultLibFileName below, we must also provide the - // directory containing the file. - // Otherwise TypeScript looks in C:\lib.xxx.d.ts for the default lib. - return path.dirname( - this.getDefaultLibFileName({target: ts.ScriptTarget.ES5})); - } - - getDefaultLibFileName(options: ts.CompilerOptions): string { - if (this.bazelOpts.nodeModulesPrefix) { - return path.join( - this.bazelOpts.nodeModulesPrefix, 'typescript/lib', - ts.getDefaultLibFileName({target: ts.ScriptTarget.ES5})); - } - return this.delegate.getDefaultLibFileName(options); - } - - realpath(s: string): string { - // tsc-wrapped relies on string matching of file paths for things like the - // file cache and for strict deps checking. - // TypeScript will try to resolve symlinks during module resolution which - // makes our checks fail: the path we resolved as an input isn't the same - // one the module resolver will look for. - // See https://github.com/Microsoft/TypeScript/pull/12020 - // So we simply turn off symlink resolution. - return s; - } - - // Delegate everything else to the original compiler host. - - getCanonicalFileName(path: string) { - return this.delegate.getCanonicalFileName(path); - } - - getCurrentDirectory(): string { - return this.delegate.getCurrentDirectory(); - } - - useCaseSensitiveFileNames(): boolean { - return this.delegate.useCaseSensitiveFileNames(); - } - - getNewLine(): string { - return this.delegate.getNewLine(); - } - - getDirectories(path: string) { - return this.delegate.getDirectories ? this.delegate.getDirectories(path) : - []; - } - - readFile(fileName: string): string|undefined { - return this.delegate.readFile(fileName); - } - - trace(s: string): void { - console.error(s); - } -}
diff --git a/packages/concatjs/internal/tsc_wrapped/compiler_host_test.ts b/packages/concatjs/internal/tsc_wrapped/compiler_host_test.ts deleted file mode 100644 index 93bc096..0000000 --- a/packages/concatjs/internal/tsc_wrapped/compiler_host_test.ts +++ /dev/null
@@ -1,115 +0,0 @@ -import 'jasmine'; - -import * as ts from 'typescript'; - -import {CompilerHost} from './compiler_host'; -import {BazelOptions} from './tsconfig'; -import { FileLoader } from './cache'; - -describe('compiler host', () => { - describe('computes the amd module name of a .ts source file', () => { - const options: ts.CompilerOptions = { - rootDirs: [], - rootDir: 'base', - outDir: 'out', - }; - const bazelOptions: BazelOptions = { - package: 'path/to/package', - compilationTargetSrc: [ - 'path/to/package/index.ts', - 'path/to/package/root_dir/index.ts', - 'test.ts', - ], - workspaceName: 'my_wksp', - } as any; - - const defaultHost = - new CompilerHost([], options, bazelOptions, null as any, null as any); - // A module is a file with at least an import or export statement. - function createTsModule(filename: string) { - return ts.createSourceFile(filename, 'export {}', ts.ScriptTarget.ES2015); - } - - it('should name a module after the workspace and filename', () => { - expect(defaultHost.amdModuleName(createTsModule('test.ts'))) - .toBe('my_wksp/test'); - }); - - it('should not provide a name for files that are not in the compilation unit', - () => { - expect( - defaultHost.amdModuleName(createTsModule('some_other_file.d.ts'))) - .toBeUndefined(); - }); - - it('should name the index file with a short name', () => { - const host = new CompilerHost( - [], options, {...bazelOptions, moduleName: 'my_lib'}, null as any, - null as any); - expect(host.amdModuleName(createTsModule('path/to/package/index.ts'))) - .toBe('my_lib'); - }); - it('should name an index file under a module_root with a short name', - () => { - const host = new CompilerHost( - [], options, - {...bazelOptions, moduleName: 'my_lib', moduleRoot: 'root_dir'}, - null as any, null as any); - expect(host.amdModuleName( - createTsModule('path/to/package/root_dir/index.ts'))) - .toBe('my_lib'); - }); - - describe('#pathToModuleName', () => { - it('should escape non-identifier characters', () => { - expect(defaultHost.pathToModuleName('context', '$-!@')) - .toBe('$24$2d$21$40'); - }); - - it('should escape leading numbers', () => { - expect(defaultHost.pathToModuleName('context', '1234')).toBe('$31234'); - }); - - it('should transform slashes to dots', () => { - expect(defaultHost.pathToModuleName('context', 'a/b')).toBe('a.b'); - }); - - it('should not escape valid identifers', () => { - expect(defaultHost.pathToModuleName('context', 'a1/b2')).toBe('a1.b2'); - }); - }); - }); - - describe('#getSourceFile', () => { - - it('should not leak generated AMD module name between compilations with cache', () => { - const compilerOpts: ts.CompilerOptions = { - rootDirs: ['.'], - rootDir: '.', - outDir: './dist', - module: ts.ModuleKind.AMD, - }; - const bazelOptions = { - workspaceName: 'my_wksp', - package: 'src/test', - compilationTargetSrc: ["test.ts"] - }; - const originalFile = ts.createSourceFile('test.ts', 'export const X = 1;', - ts.ScriptTarget.ES2015, true); - const fileLoader: FileLoader = { - fileExists: () => true, - loadFile: () => originalFile, - }; - const tsHost = ts.createCompilerHost(compilerOpts, true); - const umdBuildHost = new CompilerHost([], compilerOpts, - bazelOptions as any, tsHost, fileLoader); - const es2015BuildHost = new CompilerHost([], {...compilerOpts, module: ts.ModuleKind.ES2015}, - bazelOptions as any, tsHost, fileLoader); - - expect(umdBuildHost.getSourceFile('test.ts', ts.ScriptTarget.ES2015).moduleName) - .toBe('my_wksp/test'); - expect(es2015BuildHost.getSourceFile('test.ts', ts.ScriptTarget.ES2015).moduleName) - .toBe(undefined, 'Expected source file to not have module name from previous host.'); - }); - }); -});
diff --git a/packages/concatjs/internal/tsc_wrapped/diagnostics.ts b/packages/concatjs/internal/tsc_wrapped/diagnostics.ts deleted file mode 100644 index 53f2928..0000000 --- a/packages/concatjs/internal/tsc_wrapped/diagnostics.ts +++ /dev/null
@@ -1,167 +0,0 @@ -/** - * @fileoverview extensions to TypeScript functionality around error handling - * (ts.Diagnostics). - */ - -import * as ts from 'typescript'; - -import {BazelOptions} from './tsconfig'; - -/** - * If the current compilation was a compilation test expecting certain - * diagnostics, filter out the expected diagnostics, and add new diagnostics - * (aka errors) for non-matched diagnostics. - */ -export function filterExpected( - bazelOpts: BazelOptions, diagnostics: ts.Diagnostic[], - formatFn = uglyFormat): ts.Diagnostic[] { - if (!bazelOpts.expectedDiagnostics.length) return diagnostics; - - // The regex contains two parts: - // 1. Optional position: '\(5,1\)' - // 2. Required TS error: 'TS2000: message text.' - // Need triple escapes because the expected diagnostics that we're matching - // here are regexes, too. - const ERROR_RE = /^(?:\\\((\d*),(\d*)\\\).*)?TS(-?\d+):(.*)/; - const incorrectErrors = - bazelOpts.expectedDiagnostics.filter(e => !e.match(ERROR_RE)); - if (incorrectErrors.length) { - const msg = `Expected errors must match regex ${ERROR_RE}\n\t` + - `expected errors are "${incorrectErrors.join(', ')}"`; - return [{ - file: undefined!, - start: 0, - length: 0, - messageText: msg, - category: ts.DiagnosticCategory.Error, - code: 0, - }]; - } - - // ExpectedDiagnostics represents the "expected_diagnostics" users provide in - // the BUILD file. It is used for easier comparsion with the actual - // diagnostics. - interface ExpectedDiagnostics { - line: number; - column: number; - expected: string; - code: number; - regexp: RegExp; - matched: boolean; - } - - const expectedDiags: ExpectedDiagnostics[] = - bazelOpts.expectedDiagnostics.map(expected => { - const m = expected.match(/^(?:\\\((\d*),(\d*)\\\).*)?TS(-?\d+):(.*)$/); - if (!m) { - throw new Error( - 'Incorrect expected error, did you forget character escapes in ' + - expected); - } - const [, lineStr, columnStr, codeStr, regexp] = m; - const [line, column, code] = [lineStr, columnStr, codeStr].map(str => { - const i = Number(str); - if (Number.isNaN(i)) { - return 0; - } - return i; - }); - return { - line, - column, - expected, - code, - regexp: new RegExp(regexp), - matched: false, - }; - }); - - const unmatchedDiags = diagnostics.filter(diag => { - let line = -1; - let character = -1; - if (diag.file !== undefined && diag.start !== undefined) { - ({line, character} = - ts.getLineAndCharacterOfPosition(diag.file, diag.start)); - } - let matched = false; - const msg = formatFn(bazelOpts.target, [diag]); - // checkDiagMatchesExpected checks if the expected diagnostics matches the - // actual diagnostics. - const checkDiagMatchesExpected = - (expDiag: ExpectedDiagnostics, diag: ts.Diagnostic) => { - if (expDiag.code !== diag.code || msg.search(expDiag.regexp) === -1) { - return false; - } - // line and column are optional fields, only check them if they - // are explicitly specified. - // line and character are zero based. - if (expDiag.line !== 0 && expDiag.line !== line + 1) { - return false; - } - if (expDiag.column !== 0 && expDiag.column !== character + 1) { - return false; - } - return true; - }; - - for (const expDiag of expectedDiags) { - if (checkDiagMatchesExpected(expDiag, diag)) { - expDiag.matched = true; - matched = true; - // continue, one diagnostic may match multiple expected errors. - } - } - return !matched; - }); - - const unmatchedErrors = expectedDiags.filter(err => !err.matched).map(err => { - const file = ts.createSourceFile( - bazelOpts.target, '/* fake source as marker */', - ts.ScriptTarget.Latest); - const messageText = - `Expected a compilation error matching ${JSON.stringify(err.expected)}`; - return { - file, - start: 0, - length: 0, - messageText, - category: ts.DiagnosticCategory.Error, - code: err.code, - }; - }); - - return unmatchedDiags.concat(unmatchedErrors); -} - -/** - * Formats the given diagnostics, without pretty printing. Without colors, it's - * better for matching against programmatically. - * @param target The bazel target, e.g. //my/package:target - */ -export function uglyFormat( - target: string, diagnostics: ReadonlyArray<ts.Diagnostic>): string { - const diagnosticsHost: ts.FormatDiagnosticsHost = { - getCurrentDirectory: () => ts.sys.getCurrentDirectory(), - getNewLine: () => ts.sys.newLine, - // Print filenames including their relativeRoot, so they can be located on - // disk - getCanonicalFileName: (f: string) => f - }; - return ts.formatDiagnostics(diagnostics, diagnosticsHost); -} - -/** - * Pretty formats the given diagnostics (matching the --pretty tsc flag). - * @param target The bazel target, e.g. //my/package:target - */ -export function format( - target: string, diagnostics: ReadonlyArray<ts.Diagnostic>): string { - const diagnosticsHost: ts.FormatDiagnosticsHost = { - getCurrentDirectory: () => ts.sys.getCurrentDirectory(), - getNewLine: () => ts.sys.newLine, - // Print filenames including their relativeRoot, so they can be located on - // disk - getCanonicalFileName: (f: string) => f - }; - return ts.formatDiagnosticsWithColorAndContext(diagnostics, diagnosticsHost); -}
diff --git a/packages/concatjs/internal/tsc_wrapped/diagnostics_test.ts b/packages/concatjs/internal/tsc_wrapped/diagnostics_test.ts deleted file mode 100644 index 8acf212..0000000 --- a/packages/concatjs/internal/tsc_wrapped/diagnostics_test.ts +++ /dev/null
@@ -1,67 +0,0 @@ -import 'jasmine'; -import * as ts from 'typescript'; - -import * as diagnostics from './diagnostics'; -import {BazelOptions} from './tsconfig'; - -describe('diagnostics', () => { - describe('expected diagnostics', () => { - const file = ts.createSourceFile( - 'test.ts', '/* used for testing */', ts.ScriptTarget.Latest); - function diag(code: number, messageText: string) { - const category = ts.DiagnosticCategory.Error; - return {file, code, messageText, start: 0, length: 0, category}; - } - function filter(expectedDiagnostics: string[], diags: ts.Diagnostic[]) { - const opts = { - target: '//javascript/typescript/fake:target', - expectedDiagnostics - } as BazelOptions; - return diagnostics.filterExpected(opts, diags) - .map(d => `TS${d.code}:${d.messageText}`); - } - - it('filters expected diagnostics', () => { - expect(filter(['TS1234:very.*borked'], [ - diag(1234, 'the target is very badly borked'), - diag(1234, 'the target is very sadly borked'), - ])).toEqual([]); - }); - - it('returns unmatched expected diagnostics', () => { - expect(filter( - ['TS1234:very.*borked', 'TS1234:not matching', 'TS1235:very'], - [diag(1234, 'the target is very badly borked')])) - .toEqual([ - 'TS1234:Expected a compilation error matching "TS1234:not matching"', - 'TS1235:Expected a compilation error matching "TS1235:very"', - ]); - }); - - it('returns unmatched diagnostics', () => { - expect(filter( - ['TS1234:very.*borked'], - [ - diag(1234, 'the target is very badly borked'), - diag(5678, 'the target is very badly borked'), - diag(1234, 'text not matching'), - ])) - .toEqual([ - 'TS5678:the target is very badly borked', - 'TS1234:text not matching', - ]); - }); - - it('throws when a charater is not escaped', () => { - expect(() => filter(['TS1234:unescaped \n newline'], [])) - .toThrowError( - 'Incorrect expected error, did you forget character escapes in ' + - 'TS1234:unescaped \n newline'); - }); - - it('handle negative diagnostic codes', () => { - expect(filter(['TS-999:custom error'], [diag(-999, 'custom error')])) - .toEqual([]); - }); - }); -});
diff --git a/packages/concatjs/internal/tsc_wrapped/index.ts b/packages/concatjs/internal/tsc_wrapped/index.ts deleted file mode 100644 index 725c2a9..0000000 --- a/packages/concatjs/internal/tsc_wrapped/index.ts +++ /dev/null
@@ -1,7 +0,0 @@ -export * from './tsconfig'; -export * from './cache'; -export * from './compiler_host'; -export * from './diagnostics'; -export * from './worker'; -export * from './manifest'; -export * from './plugin_api';
diff --git a/packages/concatjs/internal/tsc_wrapped/manifest.ts b/packages/concatjs/internal/tsc_wrapped/manifest.ts deleted file mode 100644 index 3642dc7..0000000 --- a/packages/concatjs/internal/tsc_wrapped/manifest.ts +++ /dev/null
@@ -1,54 +0,0 @@ -/** - * @fileoverview utilities to construct a static graph representation of the - * import graph discovered in typescript inputs. - */ - -import * as tsickle from 'tsickle'; - -/** - * Recursively walk the import graph provided by tsickle, populating entries - * in the result map such that if foo imports bar, foo will appear before bar - * in the map. - */ -function topologicalSort( - result: tsickle.FileMap<boolean>, current: string, - modulesManifest: tsickle.ModulesManifest, - visiting: tsickle.FileMap<boolean>) { - const referencedModules = modulesManifest.getReferencedModules(current); - if (!referencedModules) return; // not in the local set of sources. - for (const referencedModule of referencedModules) { - const referencedFileName = - modulesManifest.getFileNameFromModule(referencedModule); - if (!referencedFileName) continue; // Ambient modules. - if (!result[referencedFileName]) { - if (visiting[referencedFileName]) { - const path = [current, ...Object.keys(visiting)].join(' ->\n'); - throw new Error(`\n\nCyclical dependency between files:\n${path}\n`); - } - visiting[referencedFileName] = true; - topologicalSort(result, referencedFileName, modulesManifest, visiting); - delete visiting[referencedFileName]; - } - } - result[current] = true; -} - -/** - * Create the contents of the .es5.MF file which propagates partial ordering of - * the import graph to later actions. - * Each line in the resulting text corresponds with a workspace-relative file - * path, and the lines are ordered to match the expected load order in a - * browser. - */ -export function constructManifest( - modulesManifest: tsickle.ModulesManifest, - host: {relativeOutputPath: (f: string) => string}): string { - const result: tsickle.FileMap<boolean> = {}; - for (const file of modulesManifest.fileNames) { - topologicalSort(result, file, modulesManifest, {}); - } - - // NB: The object literal maintains insertion order. - return Object.keys(result).map(fn => host.relativeOutputPath(fn)).join('\n') + - '\n'; -}
diff --git a/packages/concatjs/internal/tsc_wrapped/package.json b/packages/concatjs/internal/tsc_wrapped/package.json deleted file mode 100644 index 497cfe6..0000000 --- a/packages/concatjs/internal/tsc_wrapped/package.json +++ /dev/null
@@ -1,13 +0,0 @@ -{ - "description": "legacy build-time dependencies to compile and run tsc_wrapped", - "devDependencies": { - "@types/jasmine": "2.8.2", - "@types/node": "10.12.20", - "@types/tmp": "0.0.33", - "protobufjs": "5.0.3", - "tmp": "0.0.33", - "tsickle": "0.28.0", - "tsutils": "3.21.0", - "typescript": "2.7.2" - } -}
diff --git a/packages/concatjs/internal/tsc_wrapped/perf_trace.ts b/packages/concatjs/internal/tsc_wrapped/perf_trace.ts deleted file mode 100644 index c1f44e1..0000000 --- a/packages/concatjs/internal/tsc_wrapped/perf_trace.ts +++ /dev/null
@@ -1,96 +0,0 @@ -/** - * @license - * Copyright 2017 The Bazel Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * perf_trace records traces in the Chrome Trace format (which is actually used - * for more than just Chrome). See: - * https://github.com/catapult-project/catapult/blob/master/tracing/README.md - */ - -import * as fs from 'fs'; - -type Microseconds = number; - -/** @return a high-res timestamp of the current time. */ -function now(): Microseconds { - const [sec, nsec] = process.hrtime(); - return (sec * 1e6) + (nsec / 1e3); -} - -/** - * The type of entries in the Chrome Trace format: - * https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit - * Field names are chosen to match the JSON format. - */ -declare interface Event { - name: string; - ph: 'B'|'E'|'X'|'C'; - pid: number; // Required field in the trace viewer, but we don't use it. - ts: Microseconds; - dur?: Microseconds; - args?: any; -} - -const events: Event[] = []; - -/** wrap wraps enter()/leave() calls around a block of code. */ -export function wrap<T>(name: string, f: () => T): T { - const start = now(); - try { - return f(); - } finally { - const end = now(); - events.push({name, ph: 'X', pid: 1, ts: start, dur: (end - start)}); - } -} - -/** - * Records the execution of the given async function by invoking it. Execution - * is recorded until the async function completes. - */ -export async function wrapAsync<T>(name: string, f: () => Promise<T>): Promise<T> { - const start = now(); - try { - return await f(); - } finally { - const end = now(); - events.push({name, ph: 'X', pid: 1, ts: start, dur: (end - start)}); - } -} - -/** - * counter records a snapshot of counts. The counter name identifies a - * single graph, while the counts object provides data for each count - * of a line on the stacked bar graph. - */ -export function counter(name: string, counts: {[name: string]: number}) { - events.push({name, ph: 'C', pid: 1, ts: now(), args: counts}); -} - -/** write writes the trace in Chrome Trace format to a given path. */ -export function write(path: string) { - fs.writeFileSync(path, JSON.stringify(events), {encoding: 'utf8'}); -} - -/** Record the current heap usage to the performance trace. */ -export function snapshotMemoryUsage() { - const snapshot = process.memoryUsage(); - // The counter displays as a stacked bar graph, so compute metrics - // that sum to the appropriate total. - const unused = snapshot.heapTotal - snapshot.heapUsed; - counter('memory', {'used': snapshot.heapUsed, 'unused': unused}); -}
diff --git a/packages/concatjs/internal/tsc_wrapped/plugin_api.ts b/packages/concatjs/internal/tsc_wrapped/plugin_api.ts deleted file mode 100644 index 5dbf497..0000000 --- a/packages/concatjs/internal/tsc_wrapped/plugin_api.ts +++ /dev/null
@@ -1,117 +0,0 @@ -/** - * @license - * Copyright 2017 The Bazel Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview - * Provides APIs for extending TypeScript command-line compiler. - * It's roughly analogous to how the Language Service allows plugins. - */ - -import * as ts from 'typescript'; - -export interface PluginCompilerHost extends ts.CompilerHost { - /** - * Absolute file paths which should be included in the initial ts.Program. - * In vanilla tsc, these are the ts.ParsedCommandLine#fileNames - */ - inputFiles: ReadonlyArray<string>; -} - -/** - * This API is simpler than LanguageService plugins. - * It's used for plugins that only target the command-line and never run in an - * editor context. - * - * One instance of the TscPlugin will be created for each execution of the compiler, so it is - * safe for these plugins to hold state that's local to one execution. - * - * The methods on the plugin will be called in the order shown below: - * - wrapHost to intercept CompilerHost methods and contribute inputFiles to the program - * - setupCompilation to capture the ts.Program - * - createTransformers once it's time to emit - */ -export interface EmitPlugin { - /** - * Allow plugins to add additional files to the program. - * For example, Angular creates ngsummary and ngfactory files. - * These files must be in the program since there may be incoming references to the symbols. - * @param inputFiles the files that were part of the original program - * @param compilerHost: the original host (likely a ts.CompilerHost) that we can delegate to - */ - wrapHost?(compilerHost: ts.CompilerHost, inputFiles: string[], options: ts.CompilerOptions): PluginCompilerHost; - - setupCompilation(program: ts.Program, oldProgram?: ts.Program): { - ignoreForDiagnostics: Set<ts.SourceFile>, - ignoreForEmit: Set<ts.SourceFile> - }; - - getNextProgram?(): ts.Program; - - /** - * Allow plugins to contribute additional TypeScript CustomTransformers. - * These can modify the TS AST, JS AST, or .d.ts output AST. - */ - createTransformers(): ts.CustomTransformers; -} - -/** - * The proxy design pattern, allowing us to customize behavior of the delegate - * object. - * This creates a property-by-property copy of the object, so it can be mutated - * without affecting other users of the original object. - * See https://en.wikipedia.org/wiki/Proxy_pattern - */ -export function createProxy<T>(delegate: T): T { - const proxy = Object.create(null); - for (const k of Object.keys(delegate)) { - proxy[k] = function() { - return (delegate as any)[k].apply(delegate, arguments); - }; - } - return proxy; -} - -/** - * A plugin that contributes additional diagnostics during compilation. - * - * This is a more limited API than Plugin, which can overwrite any features of - * the Program. A DiagnosticPlugin can't affect the output, and can only reject - * otherwise valid programs. - * - * This means that disabling a DiagnosticPlugin is always safe. It will not - * break any downstream projects, either at build time or in production. - * - * It also lets us instrument the plugin to track performance, and tag the - * diagnostics it emits with the plugin name. - */ -export interface DiagnosticPlugin { - /** - * A brief descriptive name for the plugin. - * - * Should not include 'ts', 'typescript', or 'plugin'. - */ - readonly name: string; - - /** - * Return diagnostics for the given file. - * - * Should only include new diagnostics that your plugin is contributing. - * Should not include diagnostics from program. - */ - getDiagnostics(sourceFile: ts.SourceFile): - ReadonlyArray<Readonly<ts.Diagnostic>>; -}
diff --git a/packages/concatjs/internal/tsc_wrapped/strict_deps.ts b/packages/concatjs/internal/tsc_wrapped/strict_deps.ts deleted file mode 100644 index 447fc3e..0000000 --- a/packages/concatjs/internal/tsc_wrapped/strict_deps.ts +++ /dev/null
@@ -1,113 +0,0 @@ -/** - * @license - * Copyright 2017 The Bazel Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as path from 'path'; -import * as ts from 'typescript'; - -import * as perfTrace from './perf_trace'; -import * as pluginApi from './plugin_api'; - -export interface StrictDepsPluginConfig { - compilationTargetSrc: string[]; - allowedStrictDeps: string[]; - rootDir: string; -} - -/** The TypeScript diagnostic code for "Cannot find module ...". */ -export const TS_ERR_CANNOT_FIND_MODULE = 2307; - -/** - * The strict_deps plugin checks the imports of the compiled modules. - * - * It implements strict deps, i.e. enforces that each file in - * `config.compilationTargetSrc` only imports from files in - * `config.allowedStrictDeps`. - * - * This is used to implement strict dependency checking - - * source files in a build target may only import sources of their immediate - * dependencies, but not sources of their transitive dependencies. - * - * strict_deps also makes sure that no imports ends in '.ts'. TypeScript - * allows imports including the file extension, but our runtime loading support - * fails with it. - * - * strict_deps currently does not check ambient/global definitions. - */ -export class Plugin implements pluginApi.DiagnosticPlugin { - constructor( - private readonly program: ts.Program, - private readonly config: StrictDepsPluginConfig) {} - - readonly name = 'strictDeps'; - - getDiagnostics(sourceFile: ts.SourceFile) { - return checkModuleDeps( - sourceFile, this.program.getTypeChecker(), - this.config.allowedStrictDeps, this.config.rootDir); - } -} - -// Exported for testing -export function checkModuleDeps( - sf: ts.SourceFile, tc: ts.TypeChecker, allowedDeps: string[], - rootDir: string): ts.Diagnostic[] { - function stripExt(fn: string) { - return fn.replace(/(\.d)?\.tsx?$/, ''); - } - const allowedMap: {[fileName: string]: boolean} = {}; - for (const d of allowedDeps) allowedMap[stripExt(d)] = true; - - const result: ts.Diagnostic[] = []; - for (const stmt of sf.statements) { - if (stmt.kind !== ts.SyntaxKind.ImportDeclaration && - stmt.kind !== ts.SyntaxKind.ExportDeclaration) { - continue; - } - const id = stmt as ts.ImportDeclaration | ts.ExportDeclaration; - const modSpec = id.moduleSpecifier; - if (!modSpec) continue; // E.g. a bare "export {x};" - - const sym = tc.getSymbolAtLocation(modSpec); - if (!sym || !sym.declarations || sym.declarations.length < 1) { - continue; - } - const declFileNames = - sym.declarations.map(decl => decl.getSourceFile().fileName); - if (declFileNames.find( - declFileName => !!allowedMap[stripExt(declFileName)])) { - continue; - } - const importNames = declFileNames.map( - declFileName => path.posix.relative(rootDir, declFileName)); - - const extraDeclarationLocationsMessage = (importNames.length < 2) ? - '' : - `(It is also declared in ${importNames.slice(1).join(', ')}) `; - result.push({ - file: sf, - start: modSpec.getStart(), - length: modSpec.getEnd() - modSpec.getStart(), - messageText: `transitive dependency on ${importNames[0]} not allowed. ` + - extraDeclarationLocationsMessage + - `Please add the BUILD target to your rule's deps.`, - category: ts.DiagnosticCategory.Error, - // semantics are close enough, needs taze. - code: TS_ERR_CANNOT_FIND_MODULE, - }); - } - return result; -}
diff --git a/packages/concatjs/internal/tsc_wrapped/strict_deps_test.ts b/packages/concatjs/internal/tsc_wrapped/strict_deps_test.ts deleted file mode 100644 index 1e3ae47..0000000 --- a/packages/concatjs/internal/tsc_wrapped/strict_deps_test.ts +++ /dev/null
@@ -1,157 +0,0 @@ -/** - * @license - * Copyright 2017 The Bazel Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import 'jasmine'; -import * as ts from 'typescript'; - -import {checkModuleDeps} from './strict_deps'; - -describe('strict deps', () => { - // Cache ASTs that are not part of the test to avoid parsing lib.d.ts over and - // over again. - const astCache = new Map<string, ts.SourceFile>(); - - function createProgram(files: ts.MapLike<string>) { - const options: ts.CompilerOptions = { - noResolve: true, - baseUrl: '/src', - rootDirs: ['/src', '/src/blaze-bin'], - paths: {'*': ['*', 'blaze-bin/*']}, - }; - // Fake compiler host relying on `files` above. - const host = ts.createCompilerHost(options); - const originalGetSourceFile = host.getSourceFile.bind(host); - host.getSourceFile = (fileName: string) => { - if (!files[fileName]) { - if (astCache.has(fileName)) return astCache.get(fileName); - const file = originalGetSourceFile(fileName, ts.ScriptTarget.Latest); - astCache.set(fileName, file!); - return file; - } - return ts.createSourceFile( - fileName, files[fileName], ts.ScriptTarget.Latest); - }; - - // Fake module resolution host relying on `files` above. - host.fileExists = (f) => !!files[f]; - host.directoryExists = () => true; - host.realpath = (f) => f; - const rf = host.readFile.bind(host); - host.readFile = (f) => files[f] || rf(f); - host.getCurrentDirectory = () => '/src'; - host.getDirectories = (path) => []; - - const p = ts.createProgram(Object.keys(files), options, host); - const diags = [...ts.getPreEmitDiagnostics(p)]; - if (diags.length > 0) { - throw new Error(ts.formatDiagnostics(diags, { - getCurrentDirectory: () => ts.sys.getCurrentDirectory(), - getNewLine: () => ts.sys.newLine, - getCanonicalFileName: (f: string) => f, - })); - } - return p; - } - - it('reports errors for transitive dependencies', () => { - const p = createProgram({ - '/src/p/sd1.ts': 'export let x = 1;', - '/src/p/sd2.ts': `import {x} from "./sd1"; - export let y = x;`, - '/src/p/sd3.ts': `import {y} from "./sd2"; - import {x} from "./sd1"; - export let z = x + y;`, - }); - const diags = checkModuleDeps( - p.getSourceFile('p/sd3.ts')!, p.getTypeChecker(), ['/src/p/sd2.ts'], - '/src'); - expect(diags.length).toBe(1); - expect(diags[0].messageText) - .toMatch(/transitive dependency on p\/sd1.ts not allowed/); - }); - - it('reports errors for exports', () => { - const p = createProgram({ - '/src/p/sd1.ts': 'export let x = 1;', - '/src/p/sd2.ts': `import {x} from "./sd1"; - export let y = x;`, - '/src/p/sd3.ts': `export {x} from "./sd1";`, - }); - const diags = checkModuleDeps( - p.getSourceFile('p/sd3.ts')!, p.getTypeChecker(), ['/src/p/sd2.ts'], - '/src'); - expect(diags.length).toBe(1); - expect(diags[0].messageText) - .toMatch(/transitive dependency on p\/sd1.ts not allowed/); - }); - - it('supports files mapped in blaze-bin', () => { - const p = createProgram({ - '/src/blaze-bin/p/sd1.ts': 'export let x = 1;', - '/src/blaze-bin/p/sd2.ts': `import {x} from "./sd1"; - export let y = x;`, - '/src/p/sd3.ts': `import {y} from "./sd2"; - import {x} from "./sd1"; - export let z = x + y;`, - }); - const diags = checkModuleDeps( - p.getSourceFile('/src/p/sd3.ts')!, p.getTypeChecker(), - ['/src/blaze-bin/p/sd2.ts'], '/src'); - expect(diags.length).toBe(1); - expect(diags[0].messageText) - .toMatch(/dependency on blaze-bin\/p\/sd1.ts not allowed/); - }); - - it('supports .d.ts files', () => { - const p = createProgram({ - '/src/blaze-bin/p/sd1.d.ts': 'export declare let x: number;', - '/src/blaze-bin/p/sd2.d.ts': `import {x} from "./sd1"; - export declare let y: number;`, - '/src/p/sd3.ts': `import {y} from "./sd2"; - import {x} from "./sd1"; - export let z = x + y;`, - }); - const diags = checkModuleDeps( - p.getSourceFile('/src/p/sd3.ts')!, p.getTypeChecker(), - ['/src/blaze-bin/p/sd2.d.ts'], '/src'); - expect(diags.length).toBe(1); - expect(diags[0].messageText) - .toMatch(/dependency on blaze-bin\/p\/sd1.d.ts not allowed/); - }); - - it('allows multiple declarations of the same clutz-generated module', () => { - const p = createProgram({ - '/src/blaze-bin/p/js1.d.ts': `declare module 'goog:thing' {}`, - '/src/blaze-bin/p/js2.d.ts': `declare module 'goog:thing' {}`, - '/src/blaze-bin/p/js3.d.ts': `declare module 'goog:thing' {}`, - // Import from the middle one, to be sure it doesn't pass just because the - // order so happens that we checked the declaration from the first one - '/src/p/my.ts': `import {} from 'goog:thing'; // taze: from //p:js2`, - }); - const good = checkModuleDeps( - p.getSourceFile('/src/p/my.ts')!, p.getTypeChecker(), - ['/src/blaze-bin/p/js2.d.ts'], '/src'); - expect(good.length).toBe(0); - - const bad = checkModuleDeps( - p.getSourceFile('/src/p/my.ts')!, p.getTypeChecker(), [], '/src'); - expect(bad.length).toBe(1); - expect(bad[0].messageText) - .toContain( - '(It is also declared in blaze-bin/p/js2.d.ts, blaze-bin/p/js3.d.ts)'); - }); -});
diff --git a/packages/concatjs/internal/tsc_wrapped/test_support.ts b/packages/concatjs/internal/tsc_wrapped/test_support.ts deleted file mode 100644 index 5f592e6..0000000 --- a/packages/concatjs/internal/tsc_wrapped/test_support.ts +++ /dev/null
@@ -1,41 +0,0 @@ -/** - * @license - * Copyright 2017 The Bazel Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** @fileoverview Helper functions for tests. */ - -import * as fs from 'fs'; - -import {FileCache} from './cache'; - -export function writeTempFile(name: string, contents: string): string { - // TEST_TMPDIR is set by bazel. - const fn = (process.env['TEST_TMPDIR'] || '/tmp') + '/tmp.' + - (Math.random() * 1000000).toFixed(0) + '.' + name; - fs.writeFileSync(fn, contents); - return fn; -} - -let digestNumber = 0; - -export function invalidateFileCache(fc: FileCache, ...fileNames: string[]) { - const digests = new Map<string, string>(); - for (const fp of fileNames) { - digestNumber++; - digests.set(fp, `${fp}${digestNumber}`); - } - fc.updateCache(digests); -}
diff --git a/packages/concatjs/internal/tsc_wrapped/tsc_wrapped.ts b/packages/concatjs/internal/tsc_wrapped/tsc_wrapped.ts deleted file mode 100644 index 04b4b21..0000000 --- a/packages/concatjs/internal/tsc_wrapped/tsc_wrapped.ts +++ /dev/null
@@ -1,694 +0,0 @@ -import * as fs from 'fs'; -import * as path from 'path'; -import * as ts from 'typescript'; - -// Tsickle is optional, but this import is just used for typechecking. -import type * as tsickle from 'tsickle'; - -import {Plugin as BazelConformancePlugin} from '../tsetse/runner'; - -import {CachedFileLoader, FileLoader, ProgramAndFileCache, UncachedFileLoader} from './cache'; -import {CompilerHost} from './compiler_host'; -import * as bazelDiagnostics from './diagnostics'; -import {constructManifest} from './manifest'; -import * as perfTrace from './perf_trace'; -import {DiagnosticPlugin, PluginCompilerHost, EmitPlugin} from './plugin_api'; -import {Plugin as StrictDepsPlugin} from './strict_deps'; -import {BazelOptions, parseTsconfig, resolveNormalizedPath} from './tsconfig'; -import {debug, log, runAsWorker, runWorkerLoop} from './worker'; -import {getAngularEmitPluginOrThrow} from './angular_plugin'; - -/** - * Top-level entry point for tsc_wrapped. - */ -export async function main(args: string[]) { - if (runAsWorker(args)) { - log('Starting TypeScript compiler persistent worker...'); - runWorkerLoop(runOneBuild); - // Note: intentionally don't process.exit() here, because runWorkerLoop - // is waiting for async callbacks from node. - } else { - debug('Running a single build...'); - if (args.length === 0) throw new Error('Not enough arguments'); - if (!await runOneBuild(args)) { - return 1; - } - } - return 0; -} - -/** The one ProgramAndFileCache instance used in this process. */ -const cache = new ProgramAndFileCache(debug); - -function isCompilationTarget( - bazelOpts: BazelOptions, sf: ts.SourceFile): boolean { - if (bazelOpts.isJsTranspilation && bazelOpts.transpiledJsInputDirectory) { - // transpiledJsInputDirectory is a relative logical path, so we cannot - // compare it to the resolved, absolute path of sf here. - // compilationTargetSrc is resolved, so use that for the comparison. - return sf.fileName.startsWith(bazelOpts.compilationTargetSrc[0]); - } - return (bazelOpts.compilationTargetSrc.indexOf(sf.fileName) !== -1); -} - -/** - * Gather diagnostics from TypeScript's type-checker as well as other plugins we - * install such as strict dependency checking. - */ -export function gatherDiagnostics( - options: ts.CompilerOptions, bazelOpts: BazelOptions, program: ts.Program, - disabledTsetseRules: string[], plugins: DiagnosticPlugin[] = [], - ignoreForDiagnostics: Set<ts.SourceFile> = new Set()): ts.Diagnostic[] { - const diagnostics: ts.Diagnostic[] = []; - perfTrace.wrap('type checking', () => { - if (!bazelOpts.typeCheck) { - return; - } - // These checks mirror ts.getPreEmitDiagnostics, with the important - // exception of avoiding b/30708240, which is that if you call - // program.getDeclarationDiagnostics() it somehow corrupts the emit. - perfTrace.wrap(`global diagnostics`, () => { - diagnostics.push(...program.getOptionsDiagnostics()); - diagnostics.push(...program.getGlobalDiagnostics()); - }); - - // Check if `f` is a target for type-checking. - let isTypeCheckTarget = (f: ts.SourceFile) => - bazelOpts.typeCheckDependencies || isCompilationTarget(bazelOpts, f); - - let sourceFilesToCheck: ReadonlyArray<ts.SourceFile> = - program.getSourceFiles().filter( - f => isTypeCheckTarget(f) && !ignoreForDiagnostics.has(f)); - for (const sf of sourceFilesToCheck) { - perfTrace.wrap(`check ${sf.fileName}`, () => { - diagnostics.push(...program.getSyntacticDiagnostics(sf)); - diagnostics.push(...program.getSemanticDiagnostics(sf)); - }); - perfTrace.snapshotMemoryUsage(); - } - - // Install extra diagnostic plugins - plugins.push( - ...getCommonPlugins(options, bazelOpts, program, disabledTsetseRules)); - for (const plugin of plugins) { - perfTrace.wrap(`${plugin.name} diagnostics`, () => { - for (const sf of sourceFilesToCheck) { - perfTrace.wrap(`${plugin.name} checking ${sf.fileName}`, () => { - const pluginDiagnostics = plugin.getDiagnostics(sf).map((d) => { - return tagDiagnosticWithPlugin(plugin.name, d); - }); - diagnostics.push(...pluginDiagnostics); - }); - perfTrace.snapshotMemoryUsage(); - } - }); - } - }); - - return diagnostics; -} - -/** - * Construct diagnostic plugins that we always want included. - * - * TODO: Call sites of getDiagnostics should initialize plugins themselves, - * including these, and the arguments to getDiagnostics should be simplified. - */ -export function getCommonPlugins( - options: ts.CompilerOptions, bazelOpts: BazelOptions, program: ts.Program, - disabledTsetseRules: string[]): DiagnosticPlugin[] { - const plugins: DiagnosticPlugin[] = []; - if (!bazelOpts.disableStrictDeps) { - if (options.rootDir == null) { - throw new Error(`StrictDepsPlugin requires that rootDir be specified`); - } - plugins.push(new StrictDepsPlugin(program, { - ...bazelOpts, - rootDir: options.rootDir, - })); - } - if (!bazelOpts.isJsTranspilation) { - let tsetsePluginConstructor: - {new (program: ts.Program, disabledRules: string[]): DiagnosticPlugin} = - BazelConformancePlugin; - plugins.push(new tsetsePluginConstructor(program, disabledTsetseRules)); - } - return plugins; -} - -/** - * Returns a copy of diagnostic with one whose text has been prepended with - * an indication of what plugin contributed that diagnostic. - * - * This is slightly complicated because a diagnostic's message text can be - * split up into a chain of diagnostics, e.g. when there's supplementary info - * about a diagnostic. - */ -function tagDiagnosticWithPlugin( - pluginName: string, diagnostic: Readonly<ts.Diagnostic>): ts.Diagnostic { - const tagMessageWithPluginName = (text: string) => `[${pluginName}] ${text}`; - - let messageText; - if (typeof diagnostic.messageText === 'string') { - // The simple case, where a diagnostic's message is just a string. - messageText = tagMessageWithPluginName(diagnostic.messageText); - } else { - // In the case of a chain of messages we only want to tag the head of the - // chain, as that's the first line of message on the CLI. - const chain: ts.DiagnosticMessageChain = diagnostic.messageText; - messageText = { - ...chain, - messageText: tagMessageWithPluginName(chain.messageText) - }; - } - return { - ...diagnostic, - messageText, - }; -} - -/** - * expandSourcesFromDirectories finds any directories under filePath and expands - * them to their .js or .ts contents. - */ -function expandSourcesFromDirectories(fileList: string[], filePath: string) { - if (filePath.endsWith('.ts') || filePath.endsWith('.tsx') || - filePath.endsWith('.js')) { - fileList.push(filePath); - return; - } - - if (!fs.statSync(filePath).isDirectory()) { - // subdirectories may also contain e.g. .java files, which we ignore. - return; - } - const entries = fs.readdirSync(filePath); - for (const entry of entries) { - expandSourcesFromDirectories(fileList, path.join(filePath, entry)); - } -} - -/** - * Runs a single build, returning false on failure. This is potentially called - * multiple times (once per bazel request) when running as a bazel worker. - * Any encountered errors are written to stderr. - */ -async function runOneBuild( - args: string[], inputs?: {[path: string]: string}): Promise<boolean> { - if (args.length !== 1) { - console.error('Expected one argument: path to tsconfig.json'); - return false; - } - - perfTrace.snapshotMemoryUsage(); - - // Strip leading at-signs, used in build_defs.bzl to indicate a params file - const tsconfigFile = args[0].replace(/^@+/, ''); - const [parsed, errors, {target}] = parseTsconfig(tsconfigFile); - if (errors) { - console.error(bazelDiagnostics.format(target, errors)); - return false; - } - if (!parsed) { - throw new Error( - 'Impossible state: if parseTsconfig returns no errors, then parsed should be non-null'); - } - const {options, bazelOpts, files, disabledTsetseRules} = parsed; - - let sourceFiles: string[] = []; - if (bazelOpts.isJsTranspilation) { - // Under JS transpilations, some inputs might be directories. - for (const filePath of files) { - expandSourcesFromDirectories(sourceFiles, filePath); - } - } else { - sourceFiles = files; - } - - if (bazelOpts.maxCacheSizeMb !== undefined) { - const maxCacheSizeBytes = bazelOpts.maxCacheSizeMb * (1 << 20); - cache.setMaxCacheSize(maxCacheSizeBytes); - } else { - cache.resetMaxCacheSize(); - } - - let fileLoader: FileLoader; - if (inputs) { - fileLoader = new CachedFileLoader(cache); - // Resolve the inputs to absolute paths to match TypeScript internals - const resolvedInputs = new Map<string, string>(); - for (const key of Object.keys(inputs)) { - resolvedInputs.set(resolveNormalizedPath(key), inputs[key]); - } - cache.updateCache(resolvedInputs); - } else { - fileLoader = new UncachedFileLoader(); - } - - const diagnostics = await perfTrace.wrapAsync('createProgramAndEmit', async () => { - return (await createProgramAndEmit( - fileLoader, options, bazelOpts, sourceFiles, disabledTsetseRules)) - .diagnostics; - }); - - if (diagnostics.length > 0) { - console.error(bazelDiagnostics.format(bazelOpts.target, diagnostics)); - return false; - } - - const perfTracePath = bazelOpts.perfTracePath; - if (perfTracePath) { - log('Writing trace to', perfTracePath); - perfTrace.snapshotMemoryUsage(); - perfTrace.write(perfTracePath); - } - - return true; -} - -// We use the expected_diagnostics attribute for writing compilation tests. -// We don't want to expose it to users as a general-purpose feature, because -// we don't want users to end up using it like a fancy @ts-ignore. -// So instead it's limited to a whitelist. -const expectDiagnosticsWhitelist: string[] = [ -]; - -/** errorDiag produces an error diagnostic not bound to a file or location. */ -function errorDiag(messageText: string) { - return { - category: ts.DiagnosticCategory.Error, - code: 0, - file: undefined, - start: undefined, - length: undefined, - messageText, - }; -} - -/** - * createProgramAndEmit creates a ts.Program from the given options and emits it - * according to them (e.g. including running various plugins and tsickle). It - * returns the program and any diagnostics generated. - * - * Callers should check and emit diagnostics. - */ -export async function createProgramAndEmit( - fileLoader: FileLoader, options: ts.CompilerOptions, - bazelOpts: BazelOptions, files: string[], disabledTsetseRules: string[]): - Promise<{program?: ts.Program, diagnostics: ts.Diagnostic[]}> { - // Beware! createProgramAndEmit must not print to console, nor exit etc. - // Handle errors by reporting and returning diagnostics. - perfTrace.snapshotMemoryUsage(); - cache.resetStats(); - cache.traceStats(); - - const compilerHostDelegate = - ts.createCompilerHost({target: ts.ScriptTarget.ES5}); - - const moduleResolver = bazelOpts.isJsTranspilation ? - makeJsModuleResolver(bazelOpts.workspaceName) : - ts.resolveModuleName; - - // Files which should be allowed to be read, but aren't TypeScript code - const assets: string[] = []; - if (bazelOpts.angularCompilerOptions) { - if (bazelOpts.angularCompilerOptions.assets) { - assets.push(...bazelOpts.angularCompilerOptions.assets); - } - } - - const tsickleCompilerHost = new CompilerHost( - [...files, ...assets], options, bazelOpts, compilerHostDelegate, fileLoader, - moduleResolver); - let compilerHost: PluginCompilerHost = tsickleCompilerHost; - const diagnosticPlugins: DiagnosticPlugin[] = []; - - let angularPlugin: EmitPlugin&DiagnosticPlugin|undefined; - if (bazelOpts.angularCompilerOptions) { - try { - // Dynamically load the Angular emit plugin. - // Lazy load, so that code that does not use the plugin doesn't even - // have to spend the time to parse and load the plugin's source. - const NgEmitPluginCtor = await getAngularEmitPluginOrThrow(); - const ngOptions = bazelOpts.angularCompilerOptions; - - // Add the rootDir setting to the options passed to NgTscPlugin. - // Required so that synthetic files added to the rootFiles in the program - // can be given absolute paths, just as we do in tsconfig.ts, matching - // the behavior in TypeScript's tsconfig parsing logic. - ngOptions['rootDir'] = options.rootDir; - - angularPlugin = new NgEmitPluginCtor(ngOptions); - } catch (e) { - return { - diagnostics: [errorDiag( - 'when using `ts_library(use_angular_plugin=True)`, ' + - `you must install "@angular/compiler-cli". Error: ${e}`)] - }; - } - - diagnosticPlugins.push(angularPlugin); - - // Wrap host so that Ivy compiler can add a file to it (has synthetic types for checking templates) - // TODO(arick): remove after ngsummary and ngfactory files eliminated - compilerHost = angularPlugin!.wrapHost!(compilerHost, files, options); - } - - - const oldProgram = cache.getProgram(bazelOpts.target); - const program = perfTrace.wrap( - 'createProgram', - () => ts.createProgram( - compilerHost.inputFiles, options, compilerHost, oldProgram)); - cache.putProgram(bazelOpts.target, program); - - let transformers: ts.CustomTransformers = { - before: [], - after: [], - afterDeclarations: [], - }; - - let ignoreForDiagnostics = new Set<ts.SourceFile>(); - - if (angularPlugin) { - // The Angular plugin (via the `wrapHost` call above) inserts additional - // "shim" files into the `ts.Program`, beyond the user's .ts files. For - // proper operation, the plugin requires two modifications to the standard - // flow of TypeScript compilation, relating to which files are either - // type-checked or emitted. - // - // In tsc_wrapped, there is already a concept of which files should be - // emitted (which is calculated from the compilation inputs, as well as any - // paths that match expected Angular shims such as ngfactory files for those - // inputs). So the `ignoreForEmit` set produced by the plugin can be - // ignored here. - - const angularSetup = angularPlugin.setupCompilation(program); - - // Shims generated by the plugin do not benefit from normal type-checking, - // for a few reasons. - // 1) for emitted shims like ngfactory files, their proper contents are - // programmatically added via TypeScript transforms, so checking their - // initial contents is pointless and inefficient. - // 2) for non-emitted shims like the ngtypecheck files used in template - // type-checking, they are managed and checked internally via the plugin - // `getDiagnostics` method. Checking them as part of the normal - // diagnostics flow will at best produce spurious, duplicate errors that - // are not reported in the correct context, and at worst can produce - // incorrect errors. - // - // The `ignoreForDiagnostics` set informs tsc_wrapped which Angular shim - // files should be skipped when gathering diagnostics. - ignoreForDiagnostics = angularSetup.ignoreForDiagnostics; - - transformers = angularPlugin.createTransformers(); - } - - for (const pluginConfig of options['plugins'] as ts.PluginImport[] || []) { - if (pluginConfig.name === 'ts-lit-plugin') { - const litTscPlugin = - // Lazy load, so that code that does not use the plugin doesn't even - // have to spend the time to parse and load the plugin's source. - // - // tslint:disable-next-line:no-require-imports - new (require('ts-lit-plugin/lib/bazel-plugin').Plugin)( - program, pluginConfig) as DiagnosticPlugin; - diagnosticPlugins.push(litTscPlugin); - } - } - - if (!bazelOpts.isJsTranspilation) { - // If there are any TypeScript type errors abort now, so the error - // messages refer to the original source. After any subsequent passes - // (decorator downleveling or tsickle) we do not type check. - let diagnostics = gatherDiagnostics( - options, bazelOpts, program, disabledTsetseRules, diagnosticPlugins, - ignoreForDiagnostics); - if (!expectDiagnosticsWhitelist.length || - expectDiagnosticsWhitelist.some(p => bazelOpts.target.startsWith(p))) { - diagnostics = bazelDiagnostics.filterExpected( - bazelOpts, diagnostics, bazelDiagnostics.uglyFormat); - } else if (bazelOpts.expectedDiagnostics.length > 0) { - diagnostics.push(errorDiag( - `Only targets under ${ - expectDiagnosticsWhitelist.join(', ')} can use ` + - 'expected_diagnostics, but got ' + bazelOpts.target)); - } - - // The Angular plugin creates a new program with template type-check information - // This consumes (destroys) the old program so it's not suitable for re-use anymore - // Ask Angular to give us the updated reusable program. - if (angularPlugin) { - cache.putProgram(bazelOpts.target, angularPlugin.getNextProgram!()); - } - - if (diagnostics.length > 0) { - debug('compilation failed at', new Error().stack!); - return {program, diagnostics}; - } - } - - // Angular might have added files like input.ngfactory.ts or input.ngsummary.ts - // and these need to be emitted. - // TODO(arick): remove after Ivy is enabled and ngsummary/ngfactory files no longer needed - function isAngularFile(sf: ts.SourceFile) { - if (!/\.ng(factory|summary)\.ts$/.test(sf.fileName)) { - return false; - } - const base = sf.fileName.slice(0, /*'.ngfactory|ngsummary.ts'.length*/ -13); - // It's possible a file was named ngsummary.ts or ngfactory.ts but *not* synthetic - // So verify that base.ts or base.tsx was originally part of the compilation - const tsCandidate = {fileName: `${base}.ts`} as ts.SourceFile; - const tsxCandidate = {fileName: `${base}.tsx`} as ts.SourceFile; - return isCompilationTarget(bazelOpts, tsCandidate) || - isCompilationTarget(bazelOpts, tsxCandidate); - } - - // If the Angular plugin is in use, this list of files to emit should exclude - // any files defined in the `ignoreForEmit` set returned by the plugin. - // However limiting the outputs to the set of compilation target files (plus - // any Angular shims defined by `isAngularFile`) already has that effect, so - // `ignoreForEmit` does not need to be factored in here. - const compilationTargets = program.getSourceFiles().filter( - sf => isCompilationTarget(bazelOpts, sf) || isAngularFile(sf)); - - let diagnostics: ts.Diagnostic[] = []; - let useTsickleEmit = bazelOpts.tsickle; - - if (useTsickleEmit) { - diagnostics = emitWithTsickle( - program, tsickleCompilerHost, compilationTargets, options, bazelOpts, - transformers); - } else { - diagnostics = emitWithTypescript(program, compilationTargets, transformers); - } - - if (diagnostics.length > 0) { - debug('compilation failed at', new Error().stack!); - } - cache.printStats(); - return {program, diagnostics}; -} - -function emitWithTypescript( - program: ts.Program, compilationTargets: ts.SourceFile[], - transforms: ts.CustomTransformers): ts.Diagnostic[] { - const diagnostics: ts.Diagnostic[] = []; - for (const sf of compilationTargets) { - const result = program.emit( - sf, /*writeFile*/ undefined, - /*cancellationToken*/ undefined, /*emitOnlyDtsFiles*/ undefined, - transforms); - diagnostics.push(...result.diagnostics); - } - return diagnostics; -} - -/** - * Runs the emit pipeline with Tsickle transformations - goog.module rewriting - * and Closure types emitted included. - * Exported to be used by the internal global refactoring tools. - * TODO(radokirov): investigate using runWithOptions and making this private - * again, if we can make compilerHosts match. - */ -export function emitWithTsickle( - program: ts.Program, compilerHost: CompilerHost, - compilationTargets: ts.SourceFile[], options: ts.CompilerOptions, - bazelOpts: BazelOptions, - transforms: ts.CustomTransformers): ts.Diagnostic[] { - const emitResults: tsickle.EmitResult[] = []; - const diagnostics: ts.Diagnostic[] = []; - // The 'tsickle' import above is only used in type positions, so it won't - // result in a runtime dependency on tsickle. - // If the user requests the tsickle emit, then we dynamically require it - // here for use at runtime. - let optTsickle: typeof tsickle; - try { - // tslint:disable-next-line:no-require-imports - optTsickle = require('tsickle'); - } catch (e) { - if (e.code !== 'MODULE_NOT_FOUND') { - throw e; - } - throw new Error( - 'When setting bazelOpts { tsickle: true }, ' + - 'you must also add a devDependency on the tsickle npm package'); - } - perfTrace.wrap('emit', () => { - for (const sf of compilationTargets) { - perfTrace.wrap(`emit ${sf.fileName}`, () => { - emitResults.push(optTsickle.emitWithTsickle( - program, compilerHost, compilerHost, options, sf, - /*writeFile*/ undefined, - /*cancellationToken*/ undefined, /*emitOnlyDtsFiles*/ undefined, { - beforeTs: transforms.before, - afterTs: transforms.after, - afterDeclarations: transforms.afterDeclarations, - })); - }); - } - }); - const emitResult = optTsickle.mergeEmitResults(emitResults); - diagnostics.push(...emitResult.diagnostics); - - // If tsickle reported diagnostics, don't produce externs or manifest outputs. - if (diagnostics.length > 0) { - return diagnostics; - } - - let externs = '/** @externs */\n' + - '// generating externs was disabled using generate_externs=False\n'; - if (bazelOpts.tsickleGenerateExterns) { - externs = - optTsickle.getGeneratedExterns(emitResult.externs, options.rootDir!); - } - - if (!options.noEmit && bazelOpts.tsickleExternsPath) { - // Note: when tsickleExternsPath is provided, we always write a file as a - // marker that compilation succeeded, even if it's empty (just containing an - // @externs). - fs.writeFileSync(bazelOpts.tsickleExternsPath, externs); - - // When generating externs, generate an externs file for each of the input - // .d.ts files. - if (bazelOpts.tsickleGenerateExterns && - compilerHost.provideExternalModuleDtsNamespace) { - for (const extern of compilationTargets) { - if (!extern.isDeclarationFile) continue; - const outputBaseDir = options.outDir!; - const relativeOutputPath = - compilerHost.relativeOutputPath(extern.fileName); - mkdirp(outputBaseDir, path.dirname(relativeOutputPath)); - const outputPath = path.join(outputBaseDir, relativeOutputPath); - const moduleName = compilerHost.pathToModuleName('', extern.fileName); - fs.writeFileSync( - outputPath, - `goog.module('${moduleName}');\n` + - `// Export an empty object of unknown type to allow imports.\n` + - `// TODO: use typeof once available\n` + - `exports = /** @type {?} */ ({});\n`); - } - } - } - - if (!options.noEmit && bazelOpts.manifest) { - perfTrace.wrap('manifest', () => { - const manifest = - constructManifest(emitResult.modulesManifest, compilerHost); - fs.writeFileSync(bazelOpts.manifest, manifest); - }); - } - - return diagnostics; -} - -/** - * Creates directories subdir (a slash separated relative path) starting from - * base. - */ -function mkdirp(base: string, subdir: string) { - const steps = subdir.split(path.sep); - let current = base; - for (let i = 0; i < steps.length; i++) { - current = path.join(current, steps[i]); - if (!fs.existsSync(current)) fs.mkdirSync(current); - } -} - - -/** - * Resolve module filenames for JS modules. - * - * JS module resolution needs to be different because when transpiling JS we - * do not pass in any dependencies, so the TS module resolver will not resolve - * any files. - * - * Fortunately, JS module resolution is very simple. The imported module name - * must either a relative path, or the workspace root (i.e. 'google3'), - * so we can perform module resolution entirely based on file names, without - * looking at the filesystem. - */ -function makeJsModuleResolver(workspaceName: string) { - // The literal '/' here is cross-platform safe because it's matching on - // import specifiers, not file names. - const workspaceModuleSpecifierPrefix = `${workspaceName}/`; - const workspaceDir = `${path.sep}${workspaceName}${path.sep}`; - function jsModuleResolver( - moduleName: string, containingFile: string, - compilerOptions: ts.CompilerOptions, host: ts.ModuleResolutionHost): - ts.ResolvedModuleWithFailedLookupLocations { - let resolvedFileName; - if (containingFile === '') { - // In tsickle we resolve the filename against '' to get the goog module - // name of a sourcefile. - resolvedFileName = moduleName; - } else if (moduleName.startsWith(workspaceModuleSpecifierPrefix)) { - // Given a workspace name of 'foo', we want to resolve import specifiers - // like: 'foo/project/file.js' to the absolute filesystem path of - // project/file.js within the workspace. - const workspaceDirLocation = containingFile.indexOf(workspaceDir); - if (workspaceDirLocation < 0) { - return {resolvedModule: undefined}; - } - const absolutePathToWorkspaceDir = - containingFile.slice(0, workspaceDirLocation); - resolvedFileName = path.join(absolutePathToWorkspaceDir, moduleName); - } else { - if (!moduleName.startsWith('./') && !moduleName.startsWith('../')) { - throw new Error( - `Unsupported module import specifier: ${ - JSON.stringify(moduleName)}.\n` + - `JS module imports must either be relative paths ` + - `(beginning with '.' or '..'), ` + - `or they must begin with '${workspaceName}/'.`); - } - resolvedFileName = path.join(path.dirname(containingFile), moduleName); - } - return { - resolvedModule: { - resolvedFileName, - extension: ts.Extension.Js, // js can only import js - // These two fields are cargo culted from what ts.resolveModuleName - // seems to return. - packageId: undefined, - isExternalLibraryImport: false, - } - }; - } - - return jsModuleResolver; -} - - -if (require.main === module) { - // Do not call process.exit(), as that terminates the binary before - // completing pending operations, such as writing to stdout or emitting the - // v8 performance log. Rather, set the exit code and fall off the main - // thread, which will cause node to terminate cleanly. - main(process.argv.slice(2)) - .then(exitCode => process.exitCode = exitCode) - .catch(e => { - console.error(e); - process.exitCode = 1; - }); -}
diff --git a/packages/concatjs/internal/tsc_wrapped/tsc_wrapped_test.ts b/packages/concatjs/internal/tsc_wrapped/tsc_wrapped_test.ts deleted file mode 100644 index fa77e96..0000000 --- a/packages/concatjs/internal/tsc_wrapped/tsc_wrapped_test.ts +++ /dev/null
@@ -1,307 +0,0 @@ -import * as path from 'path'; -import {ModulesManifest} from 'tsickle'; -import * as ts from 'typescript'; - -import {UncachedFileLoader} from './cache'; -import {CompilerHost} from './compiler_host'; -import {constructManifest} from './manifest'; -import {writeTempFile} from './test_support'; -import {BazelOptions} from './tsconfig'; - -// tslint:disable-next-line:no-any mock for testing. -const throwingCompilerHostFake: ts.CompilerHost = null as any; - -const testFileLoader = new UncachedFileLoader(); - -const relativeOutputPath = (f: string) => f; - -type ModuleResolver = - (moduleName: string, containingFile: string, - compilerOptions: ts.CompilerOptions, host: ts.ModuleResolutionHost) => - ts.ResolvedModuleWithFailedLookupLocations; - -describe('tsc wrapped', () => { - function f( - res: ModulesManifest, fname: string, module: string, deps: string[]) { - res.addModule(fname, module); - for (let i = 0; i < deps.length; i++) { - res.addReferencedModule(fname, deps[i]); - } - return res; - } - - it('produces a topo-sorted manifest', () => { - const res = new ModulesManifest(); - f(res, 'src/f3.js', 'test$f3', ['test$f2', 'test$f1']); - f(res, 'src/f2.js', 'test$f2', - ['external$ts_source_not_included', 'test$f1']); - f(res, 'src/f1.js', 'test$f1', []); - expect(constructManifest(res, {relativeOutputPath})).toBe([ - 'src/f1.js\n', 'src/f2.js\n', 'src/f3.js\n' - ].join('')); - }); - - it('reports cyclical dependencies', () => { - const res = new ModulesManifest(); - f(res, 'src/f2', 'src$f2', ['src$f3']); - f(res, 'src/f3', 'src$f3', ['src$f1']); - f(res, 'src/f1', 'src$f1', ['src$f2']); - expect(() => constructManifest(res, {relativeOutputPath})) - .toThrowError(/src\/f2 ->\nsrc\/f3 ->\nsrc\/f1 ->\nsrc\/f2/g); - }); - - it('toposorts diamonds', () => { - // t - // l r - // b - const res = new ModulesManifest(); - f(res, 'bottom.js', 'bottom', ['left', 'right']); - f(res, 'right.js', 'right', ['top']); - f(res, 'left.js', 'left', ['top']); - f(res, 'top.js', 'top', []); - expect(constructManifest(res, {relativeOutputPath})).toBe([ - 'top.js\n', - 'left.js\n', - 'right.js\n', - 'bottom.js\n', - ].join('')); - }); - -}); - -// Create something that looks like CompilerOptions. -const COMPILER_OPTIONS: ts.CompilerOptions = { - rootDirs: [ - // Sorted by inverse length, as done by tsconfig.ts in production. - '/root/google3/blaze-out/k8-fastbuild/genfiles', - '/root/google3/blaze-out/k8-fastbuild/bin', - '/root/google3', - ], - outDir: '/root/google3/blaze-out/k8-fastbuild/bin', - rootDir: '/root/google3' -}; - - -const defaultBazelOpts = { - googmodule: true, - workspaceName: 'google3', - prelude: `goog.require('google3.third_party.javascript.tslib.tslib');`, -} as BazelOptions; - -describe('compiler host', () => { - const bazelOpts = { - ...defaultBazelOpts, - es5Mode: false, - } as BazelOptions; - - it('looks up files', () => { - const fn = writeTempFile('file_lookup', 'let x: number = 123;'); - const fn2 = writeTempFile('file_lookup2', 'let x: number = 124;'); - const host = new CompilerHost( - [fn /* but not fn2! */], COMPILER_OPTIONS, bazelOpts, - throwingCompilerHostFake, testFileLoader); - expect(host.fileExists(fn)).toBe(true); - expect(host.fileExists(fn2)).toBe(false); - }); - - describe('file writing', () => { - let writtenFiles: {[key: string]: string}; - beforeEach(() => writtenFiles = {}); - - const delegateHost = { - writeFile: (fn: string, d: string) => { - writtenFiles[fn.replace(/\\/g, '/')] = d; - } - // tslint:disable-next-line:no-any mock for testing. - } as any; - - function createFakeModuleResolver( - moduleRoots: {[moduleName: string]: string}): ModuleResolver { - return (moduleName: string, containingFile: string, - compilerOptions: ts.CompilerOptions, - host: ts.ModuleResolutionHost) => { - if (moduleName[0] === '.') { - moduleName = - path.posix.join(path.dirname(containingFile), moduleName); - } - for (const moduleRoot in moduleRoots) { - if (moduleName.indexOf(moduleRoot) === 0) { - const resolvedFileName = moduleRoots[moduleRoot] + - moduleName.substring(moduleRoot.length) + '.d.ts'; - return { - resolvedModule: {resolvedFileName, extension: ts.Extension.Dts}, - failedLookupLocations: [] - }; - } - } - - return { - resolvedModule: - {resolvedFileName: moduleName, extension: ts.Extension.Dts}, - failedLookupLocations: [] - }; - }; - } - - function createFakeGoogle3Host({ - es5 = false, - moduleRoots = {} as {[moduleName: string]: string}, - isJsTranspilation = false, - transpiledJsOutputFileName = undefined as string | undefined, - transpiledJsInputDirectory = undefined as string | undefined, - transpiledJsOutputDirectory = undefined as string | undefined, - } = {}) { - const bazelOpts = { - ...defaultBazelOpts, - es5Mode: es5, - isJsTranspilation, - transpiledJsOutputFileName, - transpiledJsInputDirectory, - transpiledJsOutputDirectory, - } as BazelOptions; - return new CompilerHost( - [], COMPILER_OPTIONS, bazelOpts, delegateHost, testFileLoader, - createFakeModuleResolver(moduleRoots)); - } - - describe('converts path to module names', () => { - let host: CompilerHost; - beforeEach(() => { - host = createFakeGoogle3Host({ - moduleRoots: { - 'module': 'path/to/module', - 'module2': 'path/to/module2', - 'path/to/module2': 'path/to/module2', - }, - }); - }); - - function expectPath(context: string, path: string) { - return expect(host.pathToModuleName(context, path)); - } - - it('mangles absolute paths', () => { - expectPath('whatever/context', 'some/absolute/module') - .toBe('google3.some.absolute.module'); - }); - - it('escapes special symbols', () => { - expectPath('', 'some|123').toBe('google3.some$7c123'); - expectPath('', '1some|').toBe('google3.1some$7c'); - expectPath('', 'bar/foo.bam.ts').toBe('google3.bar.foo$2ebam'); - expectPath('', '-foo-').toBe('google3.$2dfoo$2d'); - // Underscore is unmodified, because it is common in google3 paths. - expectPath('', 'foo_bar').toBe('google3.foo_bar'); - }); - - it('resolves paths', () => { - const context = 'path/to/module'; - - expectPath(context, './module2').toBe('google3.path.to.module2'); - expectPath(context, '././module2').toBe('google3.path.to.module2'); - expectPath(context, '../to/module2').toBe('google3.path.to.module2'); - expectPath(context, '../to/.././to/module2') - .toBe('google3.path.to.module2'); - }); - - it('ignores extra google3 sections in paths', () => { - expectPath('', 'google3/foo/bar').toBe('google3.foo.bar'); - }); - - it('resolves absolute paths', () => { - const context = 'path/to/module/dir/file'; - - expectPath(context, '/root/google3/some/file.ts') - .toBe('google3.some.file'); - expectPath(context, '/root/google3/path/to/some/file') - .toBe('google3.path.to.some.file'); - expectPath( - context, '/root/google3/blaze-out/k8-fastbuild/bin/some/file') - .toBe('google3.some.file'); - expectPath( - context, '/root/google3/blaze-out/k8-fastbuild/genfiles/some/file') - .toBe('google3.some.file'); - }); - - describe('uses module name for resolved file paths', () => { - it('for goog.module module names', () => { - expectPath('', 'module/dir/file2') - .toBe('google3.path.to.module.dir.file2'); - expectPath('', 'module2/dir/file2') - .toBe('google3.path.to.module2.dir.file2'); - }); - - it('for imports of files from the same module', () => { - const context = 'path/to/module/dir/file'; - - expectPath(context, 'module/dir/file2') - .toBe('google3.path.to.module.dir.file2'); - expectPath(context, './foo/bar') - .toBe('google3.path.to.module.dir.foo.bar'); - expectPath(context, '../foo/bar') - .toBe('google3.path.to.module.foo.bar'); - expectPath(context, 'path/to/module/dir/file2') - .toBe('google3.path.to.module.dir.file2'); - }); - - it('for imports of files from a different module', () => { - const context = 'path/to/module/dir/file'; - - expectPath(context, 'module2/dir/file') - .toBe('google3.path.to.module2.dir.file'); - expectPath(context, '../../module2/dir/file') - .toBe('google3.path.to.module2.dir.file'); - expectPath(context, 'path/to/module2/dir/file') - .toBe('google3.path.to.module2.dir.file'); - }); - }); - }); - - describe('output files', () => { - it('writes to .mjs in ES6 mode', () => { - createFakeGoogle3Host({ - es5: false, - }).writeFile('a.js', 'some.code();', false, undefined, []); - expect(Object.keys(writtenFiles)).toEqual([ - '/root/google3/blaze-out/k8-fastbuild/bin/a.mjs' - ]); - }); - - it('writes to .js in ES5 mode', () => { - createFakeGoogle3Host({ - es5: true, - }).writeFile('a/b.js', 'some.code();', false, undefined, []); - expect(Object.keys(writtenFiles)).toEqual([ - '/root/google3/blaze-out/k8-fastbuild/bin/a/b.js' - ]); - }); - - describe('transpiled JS', () => { - it('writes to transpiledJsOutputFileName', () => { - const host = createFakeGoogle3Host({ - isJsTranspilation: true, - transpiledJsOutputFileName: 'foo/bar/a/b.dev_es5.js', - }); - host.writeFile('a/b.js', 'some.code();', false, undefined, []); - expect(Object.keys(writtenFiles)).toEqual([ - '/root/google3/blaze-out/k8-fastbuild/bin/foo/bar/a/b.dev_es5.js' - ]); - }); - - it('writes to transpiledJsOutputDirectory', () => { - const host = createFakeGoogle3Host({ - isJsTranspilation: true, - transpiledJsInputDirectory: 'foo/bar/jsinputdir', - transpiledJsOutputDirectory: 'foo/bar/jsoutputdir', - }); - host.writeFile( - 'foo/bar/jsinputdir/a/b.js', 'some.code();', false, undefined, - []); - expect(Object.keys(writtenFiles)).toEqual([ - '/root/google3/blaze-out/k8-fastbuild/bin/foo/bar/jsoutputdir/a/b.js' - ]); - }); - }); - }); - }); -});
diff --git a/packages/concatjs/internal/tsc_wrapped/tsconfig.json.oss b/packages/concatjs/internal/tsc_wrapped/tsconfig.json.oss deleted file mode 100644 index 8c22290..0000000 --- a/packages/concatjs/internal/tsc_wrapped/tsconfig.json.oss +++ /dev/null
@@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "strict": true, - "types": [ - // Normally "node" should be listed here since we depend on it. - // However this library is built with vanilla tsc. - // Since the deps[] are passed into the ts.Program as files - // (starting with https://github.com/bazelbuild/rules_nodejs/pull/756) - // we cannot also list node as a types[] since vanilla tsc will load - // both into the program and get a conflict. - // Under tsc_wrapped this isn't a problem since we control the resolution. - ], - "lib": [ - "dom", - "es5", - "es2015.core", - "es2015.collection", - "es2015.iterable", - "es2015.promise", - // This will need to become es2018.asynciterable when - // bumping the version of TypeScript in rules_typescript/package.json - "esnext.asynciterable" - ] - } -}
diff --git a/packages/concatjs/internal/tsc_wrapped/tsconfig.ts b/packages/concatjs/internal/tsc_wrapped/tsconfig.ts deleted file mode 100644 index ca21ce3..0000000 --- a/packages/concatjs/internal/tsc_wrapped/tsconfig.ts +++ /dev/null
@@ -1,417 +0,0 @@ -/** - * @license - * Copyright 2017 The Bazel Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as path from 'path'; -import * as ts from 'typescript'; - - -/** - * The configuration block provided by the tsconfig "bazelOptions". - * Note that all paths here are relative to the rootDir, not absolute nor - * relative to the location containing the tsconfig file. - */ -export interface BazelOptions { - /** Name of the bazel workspace where we are building. */ - workspaceName: string; - - /** The full bazel target that is being built, e.g. //my/pkg:library. */ - target: string; - - /** The bazel package, eg my/pkg */ - package: string; - - /** If true, convert require()s into goog.module(). */ - googmodule: boolean; - - /** - * DEPRECATED. being replaced by devmode. - * If true, emit devmode output into filename.js. - * If false, emit prodmode output into filename.mjs. - */ - es5Mode: boolean; - - /** - * If true, emit devmode output into filename.js. - * If false, emit prodmode output into filename.mjs. - */ - devmode: boolean; - - /** If true, convert TypeScript code into a Closure-compatible variant. */ - tsickle: boolean; - - /** If true, generate externs from declarations in d.ts files. */ - tsickleGenerateExterns: boolean; - - /** Write generated externs to the given path. */ - tsickleExternsPath: string; - - /** Paths of declarations whose types must not appear in result .d.ts. */ - typeBlackListPaths: string[]; - - /** If true, emit Closure types in TypeScript->JS output. */ - untyped: boolean; - - /** The list of sources we're interested in (emitting and type checking). */ - compilationTargetSrc: string[]; - - /** Path to write the module dependency manifest to. */ - manifest: string; - - /** - * Whether to disable strict deps check. If true the next parameter is - * ignored. - */ - disableStrictDeps?: boolean; - - /** - * Paths of dependencies that are allowed by strict deps, i.e. that may be - * imported by the source files in compilationTargetSrc. - */ - allowedStrictDeps: string[]; - - /** Write a performance trace to this path. Disabled when falsy. */ - perfTracePath?: string; - - /** - * An additional prelude to insert after the `goog.module` call, - * e.g. with additional imports or requires. - */ - prelude: string; - - /** - * Name of the current locale if processing a locale-specific file. - */ - locale?: string; - - /** - * A list of errors this compilation is expected to generate, in the form - * "TS1234:regexp". If empty, compilation is expected to succeed. - */ - expectedDiagnostics: string[]; - - /** - * To support node_module resolution, allow TypeScript to make arbitrary - * file system access to paths under this prefix. - */ - nodeModulesPrefix: string; - - /** - * List of regexes on file paths for which we suppress tsickle's warnings. - */ - ignoreWarningPaths: string[]; - - /** - * Whether to add aliases to the .d.ts files to add the exports to the - * ಠ_ಠ.clutz namespace. - */ - addDtsClutzAliases: true; - - /** - * Whether to type check inputs that aren't srcs. Differs from - * --skipLibCheck, which skips all .d.ts files, even those which are - * srcs. - */ - typeCheckDependencies: boolean; - - /** - * The maximum cache size for bazel outputs, in megabytes. - */ - maxCacheSizeMb?: number; - - /** - * Suppress warnings about tsconfig.json properties that are overridden. - * Currently unused, remains here for backwards compat for users who set it. - */ - suppressTsconfigOverrideWarnings: boolean; - - /** - * An explicit name for this module, given by the module_name attribute on a - * ts_library. - */ - moduleName?: string; - - /** - * An explicit entry point for this module, given by the module_root attribute - * on a ts_library. - */ - moduleRoot?: string; - - /** - * If true, indicates that this job is transpiling JS sources. If true, only - * one file can appear in compilationTargetSrc, and either - * transpiledJsOutputFileName or the transpiledJs*Directory options must be - * set. - */ - isJsTranspilation?: boolean; - - /** - * The path where the file containing the JS transpiled output should be - * written. Ignored if isJsTranspilation is false. transpiledJsOutputFileName - * - */ - transpiledJsOutputFileName?: string; - - /** - * The path where transpiled JS output should be written. Ignored if - * isJsTranspilation is false. Must not be set together with - * transpiledJsOutputFileName. - */ - transpiledJsInputDirectory?: string; - - /** - * The path where transpiled JS output should be written. Ignored if - * isJsTranspilation is false. Must not be set together with - * transpiledJsOutputFileName. - */ - transpiledJsOutputDirectory?: string; - - /** - * Whether the user provided an implementation shim for .d.ts files in the - * compilation unit. - */ - hasImplementation?: boolean; - - /** - * If present, run the Angular ngtsc plugin with the given options. - */ - angularCompilerOptions?: { - [k: string]: any, - assets: string[], - // Ideally we would - // import {AngularCompilerOptions} from '@angular/compiler-cli'; - // and the type would be AngularCompilerOptions&{assets: string[]}; - // but we don't want a dependency from @bazel/typescript to @angular/compiler-cli - // as it's conceptually cyclical. - }; - - /** - * Override for ECMAScript target language level to use for devmode. - * - * This setting can be set in a user's tsconfig to override the default - * devmode target. - * - * EXPERIMENTAL: This setting is experimental and may be removed in the - * future. - */ - devmodeTargetOverride?: string; - - /** - * Whether to type check. Differs from typeCheckDependencies in that this - * avoids type checking the srcs in addition to the dependencies. - */ - typeCheck: boolean; -} - -export interface ParsedTsConfig { - options: ts.CompilerOptions; - bazelOpts: BazelOptions; - angularCompilerOptions?: {[k: string]: unknown}; - files: string[]; - disabledTsetseRules: string[]; - config: {}; -} - -// TODO(calebegg): Upstream? -interface PluginImportWithConfig extends ts.PluginImport { - [optionName: string]: string|{}; -} - -/** - * The same as Node's path.resolve, however it returns a path with forward - * slashes rather than joining the resolved path with the platform's path - * separator. - * Note that even path.posix.resolve('.') returns C:\Users\... with backslashes. - */ -export function resolveNormalizedPath(...segments: string[]): string { - return path.resolve(...segments).replace(/\\/g, '/'); -} - -/** - * Load a tsconfig.json and convert all referenced paths (including - * bazelOptions) to absolute paths. - * Paths seen by TypeScript should be absolute, to match behavior - * of the tsc ModuleResolution implementation. - * @param tsconfigFile path to tsconfig, relative to process.cwd() or absolute - * @return configuration parsed from the file, or error diagnostics - */ -export function parseTsconfig( - tsconfigFile: string, host: ts.ParseConfigHost = ts.sys): - [ParsedTsConfig|null, ts.Diagnostic[]|null, {target: string}] { - // TypeScript expects an absolute path for the tsconfig.json file - tsconfigFile = resolveNormalizedPath(tsconfigFile); - - const isUndefined = (value: any): value is undefined => value === undefined; - - // Handle bazel specific options, but make sure not to crash when reading a - // vanilla tsconfig.json. - - const readExtendedConfigFile = - (configFile: string, existingConfig?: any): {config?: any, error?: ts.Diagnostic} => { - const {config, error} = ts.readConfigFile(configFile, host.readFile); - - if (error) { - return {error}; - } - - // Allow Bazel users to control some of the bazel options. - // Since TypeScript's "extends" mechanism applies only to "compilerOptions" - // we have to repeat some of their logic to get the user's bazelOptions. - const mergedConfig = existingConfig || config; - - if (existingConfig) { - const existingBazelOpts: BazelOptions = existingConfig.bazelOptions || {}; - const newBazelBazelOpts: BazelOptions = config.bazelOptions || {}; - - mergedConfig.bazelOptions = { - ...existingBazelOpts, - - disableStrictDeps: isUndefined(existingBazelOpts.disableStrictDeps) - ? newBazelBazelOpts.disableStrictDeps - : existingBazelOpts.disableStrictDeps, - - suppressTsconfigOverrideWarnings: isUndefined(existingBazelOpts.suppressTsconfigOverrideWarnings) - ? newBazelBazelOpts.suppressTsconfigOverrideWarnings - : existingBazelOpts.suppressTsconfigOverrideWarnings, - - tsickle: isUndefined(existingBazelOpts.tsickle) - ? newBazelBazelOpts.tsickle - : existingBazelOpts.tsickle, - - googmodule: isUndefined(existingBazelOpts.googmodule) - ? newBazelBazelOpts.googmodule - : existingBazelOpts.googmodule, - - devmodeTargetOverride: isUndefined(existingBazelOpts.devmodeTargetOverride) - ? newBazelBazelOpts.devmodeTargetOverride - : existingBazelOpts.devmodeTargetOverride, - } - } - - if (config.extends) { - let extendedConfigPath = resolveNormalizedPath(path.dirname(configFile), config.extends); - if (!extendedConfigPath.endsWith('.json')) extendedConfigPath += '.json'; - - return readExtendedConfigFile(extendedConfigPath, mergedConfig); - } - - return {config: mergedConfig}; - }; - - const {config, error} = readExtendedConfigFile(tsconfigFile); - if (error) { - // target is in the config file we failed to load... - return [null, [error], {target: ''}]; - } - - const {options, errors, fileNames} = - ts.parseJsonConfigFileContent(config, host, path.dirname(tsconfigFile)); - - // Handle bazel specific options, but make sure not to crash when reading a - // vanilla tsconfig.json. - const bazelOpts: BazelOptions = config.bazelOptions || {}; - const target = bazelOpts.target; - bazelOpts.allowedStrictDeps = bazelOpts.allowedStrictDeps || []; - bazelOpts.typeBlackListPaths = bazelOpts.typeBlackListPaths || []; - bazelOpts.compilationTargetSrc = bazelOpts.compilationTargetSrc || []; - - - if (errors && errors.length) { - return [null, errors, {target}]; - } - - // Override the devmode target if devmodeTargetOverride is set - if (bazelOpts.es5Mode && bazelOpts.devmodeTargetOverride) { - switch (bazelOpts.devmodeTargetOverride.toLowerCase()) { - case 'es3': - options.target = ts.ScriptTarget.ES3; - break; - case 'es5': - options.target = ts.ScriptTarget.ES5; - break; - case 'es2015': - options.target = ts.ScriptTarget.ES2015; - break; - case 'es2016': - options.target = ts.ScriptTarget.ES2016; - break; - case 'es2017': - options.target = ts.ScriptTarget.ES2017; - break; - case 'es2018': - options.target = ts.ScriptTarget.ES2018; - break; - case 'esnext': - options.target = ts.ScriptTarget.ESNext; - break; - default: - console.error( - 'WARNING: your tsconfig.json file specifies an invalid bazelOptions.devmodeTargetOverride value of: \'${bazelOpts.devmodeTargetOverride\''); - } - } - - // Sort rootDirs with longest include directories first. - // When canonicalizing paths, we always want to strip - // `workspace/bazel-bin/file` to just `file`, not to `bazel-bin/file`. - if (options.rootDirs) options.rootDirs.sort((a, b) => b.length - a.length); - - // If the user requested goog.module, we need to produce that output even if - // the generated tsconfig indicates otherwise. - if (bazelOpts.googmodule) options.module = ts.ModuleKind.CommonJS; - - // TypeScript's parseJsonConfigFileContent returns paths that are joined, eg. - // /path/to/project/bazel-out/arch/bin/path/to/package/../../../../../../path - // We normalize them to remove the intermediate parent directories. - // This improves error messages and also matches logic in tsc_wrapped where we - // expect normalized paths. - const files = fileNames.map(f => path.posix.normalize(f)); - - // The bazelOpts paths in the tsconfig are relative to - // options.rootDir (the workspace root) and aren't transformed by - // parseJsonConfigFileContent (because TypeScript doesn't know - // about them). Transform them to also be absolute here. - bazelOpts.compilationTargetSrc = bazelOpts.compilationTargetSrc.map( - f => resolveNormalizedPath(options.rootDir!, f)); - bazelOpts.allowedStrictDeps = bazelOpts.allowedStrictDeps.map( - f => resolveNormalizedPath(options.rootDir!, f)); - bazelOpts.typeBlackListPaths = bazelOpts.typeBlackListPaths.map( - f => resolveNormalizedPath(options.rootDir!, f)); - if (bazelOpts.nodeModulesPrefix) { - bazelOpts.nodeModulesPrefix = - resolveNormalizedPath(options.rootDir!, bazelOpts.nodeModulesPrefix); - } - if (bazelOpts.angularCompilerOptions && bazelOpts.angularCompilerOptions.assets) { - bazelOpts.angularCompilerOptions.assets = bazelOpts.angularCompilerOptions.assets.map( - f => resolveNormalizedPath(options.rootDir!, f)); - } - - let disabledTsetseRules: string[] = []; - for (const pluginConfig of options['plugins'] as PluginImportWithConfig[] || - []) { - if (pluginConfig.name && pluginConfig.name === '@bazel/tsetse') { - const disabledRules = pluginConfig['disabledRules']; - if (disabledRules && !Array.isArray(disabledRules)) { - throw new Error('Disabled tsetse rules must be an array of rule names'); - } - disabledTsetseRules = disabledRules as string[]; - break; - } - } - - return [ - {options, bazelOpts, files, config, disabledTsetseRules}, null, {target} - ]; -}
diff --git a/packages/concatjs/internal/tsc_wrapped/tsconfig_test.ts b/packages/concatjs/internal/tsc_wrapped/tsconfig_test.ts deleted file mode 100644 index eac3ffe..0000000 --- a/packages/concatjs/internal/tsc_wrapped/tsconfig_test.ts +++ /dev/null
@@ -1,116 +0,0 @@ -/** - * @license - * Copyright 2017 The Bazel Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import * as ts from 'typescript'; - -import {parseTsconfig, resolveNormalizedPath} from './tsconfig'; - -describe('tsconfig', () => { - it('honors bazelOptions in the users tsconfig', () => { - const userTsconfig = { - bazelOptions: {disableStrictDeps: true}, - }; - const generatedTsconfig = { - extends: './user.tsconfig', - files: ['a.ts'], - bazelOptions: {}, - }; - const files = { - [resolveNormalizedPath('path/to/user.tsconfig.json')]: - '/*some comment*/\n' + JSON.stringify(userTsconfig), - [resolveNormalizedPath('path/to/generated.tsconfig.json')]: - JSON.stringify(generatedTsconfig), - }; - const host: ts.ParseConfigHost = { - useCaseSensitiveFileNames: true, - fileExists: (path: string) => !!files[path], - readFile: (path: string) => files[path], - readDirectory( - rootDir: string, extensions: ReadonlyArray<string>, - excludes: ReadonlyArray<string>, includes: ReadonlyArray<string>, - depth: number): string[] { - throw new Error(`unexpected readDirectory of ${rootDir}`); - }, - }; - - const [parsed, diagnostics, {target}] = - parseTsconfig('path/to/generated.tsconfig.json', host); - expect(diagnostics).toBeNull(); - if (!parsed) { - fail('Expected parsed'); - } else { - const {options, bazelOpts, files, config} = parsed; - expect(bazelOpts.disableStrictDeps).toBeTruthy(); - } - }); - - it('honors bazelOptions in recursive extends', ()=> { - const tsconfigOne = { - extends: './tsconfig-level-b.json', - files: ['a.ts'], - bazelOptions: { - disableStrictDeps: false - } - }; - - const tsconfigTwo = { - extends: './tsconfig-level-c.json', - bazelOptions: { - suppressTsconfigOverrideWarnings: true - } - }; - - const tsconfigThree = { - bazelOptions: { - tsickle: true, - suppressTsconfigOverrideWarnings: false, - disableStrictDeps: true - } - }; - - const files = { - [resolveNormalizedPath('path/to/tsconfig-level-a.json')]: JSON.stringify(tsconfigOne), - [resolveNormalizedPath('path/to/tsconfig-level-b.json')]: JSON.stringify(tsconfigTwo), - [resolveNormalizedPath('path/to/tsconfig-level-c.json')]: JSON.stringify(tsconfigThree), - }; - - const host: ts.ParseConfigHost = { - useCaseSensitiveFileNames: true, - fileExists: (path: string) => !!files[path], - readFile: (path: string) => files[path], - readDirectory( - rootDir: string, extensions: ReadonlyArray<string>, - excludes: ReadonlyArray<string>, includes: ReadonlyArray<string>, - depth: number): string[] { - return []; - }, - }; - - const [parsed, diagnostics] = - parseTsconfig('path/to/tsconfig-level-a.json', host); - expect(diagnostics).toBeNull(); - - if (!parsed) { - fail('Expected parsed'); - } else { - const {bazelOpts} = parsed; - expect(bazelOpts.tsickle).toBeTruthy(); - expect(bazelOpts.suppressTsconfigOverrideWarnings).toBeTruthy(); - expect(bazelOpts.disableStrictDeps).toBeFalsy(); - } - }) -});
diff --git a/packages/concatjs/internal/tsc_wrapped/worker.ts b/packages/concatjs/internal/tsc_wrapped/worker.ts deleted file mode 100644 index 3640937..0000000 --- a/packages/concatjs/internal/tsc_wrapped/worker.ts +++ /dev/null
@@ -1,210 +0,0 @@ -import * as path from 'path'; -import * as protobufjs from 'protobufjs'; - -// Equivalent of running node with --expose-gc -// but easier to write tooling since we don't need to inject that arg to -// nodejs_binary -if (typeof global.gc !== 'function') { - // tslint:disable-next-line:no-require-imports - require('v8').setFlagsFromString('--expose_gc'); - // tslint:disable-next-line:no-require-imports - global.gc = require('vm').runInNewContext('gc'); -} - -/** - * Whether to print debug messages (to console.error) from the debug function - * below. - */ -export const DEBUG = false; - -/** Maybe print a debug message (depending on a flag defaulting to false). */ -export function debug(...args: Array<unknown>) { - if (DEBUG) console.error.call(console, ...args); -} - -/** - * Write a message to stderr, which appears in the bazel log and is visible to - * the end user. - */ -export function log(...args: Array<unknown>) { - console.error.call(console, ...args); -} - -/** - * runAsWorker returns true if the given arguments indicate the process should - * run as a persistent worker. - */ -export function runAsWorker(args: string[]) { - return args.indexOf('--persistent_worker') !== -1; -} - -/** - * workerProto declares the static type of the object constructed at runtime by - * protobufjs, based on reading the protocol buffer definition. - */ -declare namespace workerProto { - /** Input represents the blaze.worker.Input message. */ - interface Input extends protobufjs.Message<Input> { - path: string; - /** - * In Node, digest is a Buffer. In the browser, it's a replacement - * implementation. We only care about its toString(encoding) method. - */ - digest: {toString(encoding: string): string}; - } - - /** WorkRequest repesents the blaze.worker.WorkRequest message. */ - interface WorkRequest extends protobufjs.Message<WorkRequest> { - arguments: string[]; - inputs: Input[]; - } - - // tslint:disable:variable-name reflected, constructable types. - const WorkRequest: protobufjs.Type; - const WorkResponse: protobufjs.Type; - // tslint:enable:variable-name -} - -/** - * loadWorkerPb finds and loads the protocol buffer definition for bazel's - * worker protocol using protobufjs. In protobufjs, this means it's a reflection - * object that also contains properties for the individual messages. - */ -function loadWorkerPb() { - const protoPath = - '../../third_party/github.com/bazelbuild/bazel/src/main/protobuf/worker_protocol.proto'; - - // Use node module resolution so we can find the .proto file in any of the - // root dirs - let protofile; - try { - // Look for the .proto file relative in its @bazel/typescript npm package - // location - protofile = require.resolve(protoPath); - } catch (e) { - } - if (!protofile) { - // If not found above, look for the .proto file in its rules_typescript - // workspace location - // This extra lookup should never happen in google3. It's only needed for - // local development in the rules_typescript repo. - const runfiles = process.env['BAZEL_NODE_RUNFILES_HELPER']; - if (runfiles) { - protofile = require(runfiles).resolve( - 'build_bazel_rules_nodejs/packages/concatjs/third_party/github.com/bazelbuild/bazel/src/main/protobuf/worker_protocol.proto'); - } - if (!protofile) { - throw new Error( - `cannot find worker_protocol.proto at ${protoPath} or in Runfiles`); - } - } - - const protoNamespace = protobufjs.loadSync(protofile); - if (!protoNamespace) { - throw new Error('Cannot find ' + path.resolve(protoPath)); - } - const workerpb = protoNamespace.lookup('blaze.worker'); - if (!workerpb) { - throw new Error(`Cannot find namespace blaze.worker`); - } - return workerpb as protobufjs.ReflectionObject & typeof workerProto; -} - -/** - * workerpb contains the runtime representation of the worker protocol buffer, - * including accessor for the defined messages. - */ -const workerpb = loadWorkerPb(); - -/** - * runWorkerLoop handles the interacton between bazel workers and the - * TypeScript compiler. It reads compilation requests from stdin, unmarshals the - * data, and dispatches into `runOneBuild` for the actual compilation to happen. - * - * The compilation handler is parameterized so that this code can be used by - * different compiler entry points (currently TypeScript compilation, Angular - * compilation, and the contrib vulcanize worker). - * - * It's also exposed publicly as an npm package: - * https://www.npmjs.com/package/@bazel/worker - */ -export async function runWorkerLoop( - runOneBuild: (args: string[], inputs?: {[path: string]: string}) => - boolean | Promise<boolean>) { - // Hook all output to stderr and write it to a buffer, then include - // that buffer's in the worker protcol proto's textual output. This - // means you can log via console.error() and it will appear to the - // user as expected. - let consoleOutput = ''; - process.stderr.write = - (chunk: string|Buffer, ...otherArgs: Array<unknown>): boolean => { - consoleOutput += chunk.toString(); - return true; - }; - - // Accumulator for asynchronously read input. - // protobufjs uses node's Buffer, but has its own reader abstraction on top of - // it (for browser compatiblity). It ignores Buffer's builtin start and - // offset, which means the handling code below cannot use Buffer in a - // meaningful way (such as cycling data through it). The handler below reads - // any data available on stdin, concatenating it into this buffer. It then - // attempts to read a delimited Message from it. If a message is incomplete, - // it exits and waits for more input. If a message has been read, it strips - // its data of this buffer. - let buf: Buffer = Buffer.alloc(0); - stdinLoop: for await (const chunk of process.stdin) { - buf = Buffer.concat([buf, chunk as Buffer]); - try { - const reader = new protobufjs.Reader(buf); - // Read all requests that have accumulated in the buffer. - while (reader.len - reader.pos > 0) { - const messageStart = reader.len; - const msgLength: number = reader.uint32(); - // chunk might be an incomplete read from stdin. If there are not enough - // bytes for the next full message, wait for more input. - if ((reader.len - reader.pos) < msgLength) continue stdinLoop; - - const req = workerpb.WorkRequest.decode(reader, msgLength) as - workerProto.WorkRequest; - // Once a message has been read, remove it from buf so that if we pause - // to read more input, this message will not be processed again. - buf = buf.slice(messageStart); - debug('=== Handling new build request'); - const args = req.arguments; - const inputs: {[path: string]: string} = {}; - for (const input of req.inputs) { - inputs[input.path] = input.digest.toString('hex'); - } - debug('Compiling with:\n\t' + args.join('\n\t')); - process.stdout.write( - workerpb.WorkResponse.encodeDelimited({ - exitCode: await runOneBuild(args, inputs) ? 0 : 1, - output: consoleOutput, - }).finish() as Buffer - ); - // Reset accumulated log output now that it has been printed. - consoleOutput = ''; - // Force a garbage collection pass. This keeps our memory usage - // consistent across multiple compilations, and allows the file - // cache to use the current memory usage as a guideline for expiring - // data. Note: this is intentionally not within runOneBuild(), as - // we want to gc only after all its locals have gone out of scope. - global.gc!(); - } - // All messages have been handled, make sure the invariant holds and - // Buffer is empty once all messages have been read. - if (buf.length > 0) { - throw new Error('buffer not empty after reading all messages'); - } - } catch (e) { - log('Compilation failed', e.stack); - process.stdout.write( - workerpb.WorkResponse - .encodeDelimited({exitCode: 1, output: consoleOutput}) - .finish() as Buffer - ); - // Clear buffer so the next build won't read an incomplete request. - buf = Buffer.alloc(0); - } - } -}
diff --git a/packages/concatjs/internal/tsc_wrapped/yarn.lock b/packages/concatjs/internal/tsc_wrapped/yarn.lock deleted file mode 100644 index de1c0b0..0000000 --- a/packages/concatjs/internal/tsc_wrapped/yarn.lock +++ /dev/null
@@ -1,311 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@types/jasmine@2.8.2": - version "2.8.2" - resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.8.2.tgz#6ae4d8740c0da5d5a627df725b4eed71b8e36668" - integrity sha512-RabEJPjYMpjWqW1qYj4k0rlgP5uzyguoc0yxedJdq7t5h19MYvqhjCR1evM3raZ/peHRxp1Qfl24iawvkibSug== - -"@types/node@7.0.18": - version "7.0.18" - resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.18.tgz#cd67f27d3dc0cfb746f0bdd5e086c4c5d55be173" - integrity sha1-zWfyfT3Az7dG8L3V4IbExdVb4XM= - -"@types/tmp@0.0.33": - version "0.0.33" - resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.0.33.tgz#1073c4bc824754ae3d10cfab88ab0237ba964e4d" - integrity sha1-EHPEvIJHVK49EM+riKsCN7qWTk0= - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ascli@~1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ascli/-/ascli-1.0.1.tgz#bcfa5974a62f18e81cabaeb49732ab4a88f906bc" - integrity sha1-vPpZdKYvGOgcq660lzKrSoj5Brw= - dependencies: - colour "~0.7.1" - optjs "~3.2.2" - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== - -bytebuffer@~5: - version "5.0.1" - resolved "https://registry.yarnpkg.com/bytebuffer/-/bytebuffer-5.0.1.tgz#582eea4b1a873b6d020a48d58df85f0bba6cfddd" - integrity sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0= - dependencies: - long "~3" - -camelcase@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" - integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= - -cliui@^3.0.3: - version "3.2.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" - integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0= - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi "^2.0.0" - -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= - -colour@~0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/colour/-/colour-0.7.1.tgz#9cb169917ec5d12c0736d3e8685746df1cadf778" - integrity sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g= - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -decamelize@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -glob@^7.0.5: - version "7.1.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" - integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - -invert-kv@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" - integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" - -lcid@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" - integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= - dependencies: - invert-kv "^1.0.0" - -long@~3: - version "3.2.0" - resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b" - integrity sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s= - -minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= - -minimist@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= - -mkdirp@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= - dependencies: - minimist "0.0.8" - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -optjs@~3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/optjs/-/optjs-3.2.2.tgz#69a6ce89c442a44403141ad2f9b370bd5bb6f4ee" - integrity sha1-aabOicRCpEQDFBrS+bNwvVu29O4= - -os-locale@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" - integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= - dependencies: - lcid "^1.0.0" - -os-tmpdir@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -protobufjs@5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-5.0.3.tgz#e4dfe9fb67c90b2630d15868249bcc4961467a17" - integrity sha512-55Kcx1MhPZX0zTbVosMQEO5R6/rikNXd9b6RQK4KSPcrSIIwoXTtebIczUrXlwaSrbz4x8XUVThGPob1n8I4QA== - dependencies: - ascli "~1" - bytebuffer "~5" - glob "^7.0.5" - yargs "^3.10.0" - -source-map-support@^0.5.0: - version "0.5.10" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.10.tgz#2214080bc9d51832511ee2bab96e3c2f9353120c" - integrity sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -tmp@0.0.33: - version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" - integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== - dependencies: - os-tmpdir "~1.0.2" - -tsickle@0.28.0: - version "0.28.0" - resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.28.0.tgz#6cd6fa004766c6ad9261b599c83866ee97cc7875" - integrity sha512-cb/Z4NlKMPGiIIbgmklfBJIxDl4EQoYqC+0/BnPxZWzWcUvikeOHFkkkEmabJVqKh47jUqOwU/uMAu6UvhicZg== - dependencies: - minimist "^1.2.0" - mkdirp "^0.5.1" - source-map "^0.6.0" - source-map-support "^0.5.0" - -tslib@^1.8.1: - version "1.9.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" - integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== - -tsutils@2.27.2: - version "2.27.2" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.27.2.tgz#60ba88a23d6f785ec4b89c6e8179cac9b431f1c7" - integrity sha512-qf6rmT84TFMuxAKez2pIfR8UCai49iQsfB7YWVjV1bKpy/d0PWT5rEOSM6La9PiHZ0k1RRZQiwVdVJfQ3BPHgg== - dependencies: - tslib "^1.8.1" - -typescript@2.7.2: - version "2.7.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.7.2.tgz#2d615a1ef4aee4f574425cdff7026edf81919836" - integrity sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw== - -window-size@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876" - integrity sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY= - -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" - integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -y18n@^3.2.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" - integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= - -yargs@^3.10.0: - version "3.32.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995" - integrity sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU= - dependencies: - camelcase "^2.0.1" - cliui "^3.0.3" - decamelize "^1.1.1" - os-locale "^1.4.0" - string-width "^1.0.1" - window-size "^0.1.4" - y18n "^3.2.0"
diff --git a/packages/concatjs/internal/tsconfig.json b/packages/concatjs/internal/tsconfig.json deleted file mode 100644 index f69878c..0000000 --- a/packages/concatjs/internal/tsconfig.json +++ /dev/null
@@ -1,26 +0,0 @@ -{ - "compilerOptions": { - "strict": true, - "module": "CommonJS", - "target": "ES2015", - "types": [ - "node", - ], - "lib": [ - "dom", - "es5", - "es2015.core", - "es2015.collection", - "es2015.promise", - // This will need to become es2018.asynciterable when - // bumping the version of TypeScript in rules_typescript/package.json - "es2018.asynciterable" - ] - }, - // We run the plain tsc compiler so we need to exclude files we don't want it to visit - "exclude": [ - "**/test_support.ts", - "**/*_test.ts", - "*/tests" - ] -}
diff --git a/packages/concatjs/internal/tsetse/checker.ts b/packages/concatjs/internal/tsetse/checker.ts deleted file mode 100644 index fcad3c7..0000000 --- a/packages/concatjs/internal/tsetse/checker.ts +++ /dev/null
@@ -1,242 +0,0 @@ -/** - * @fileoverview Checker contains all the information we need to perform source - * file AST traversals and report errors. - */ - -import * as ts from 'typescript'; -import {Failure, Fix} from './failure'; - - -/** - * A Handler contains a handler function and its corresponding error code so - * when the handler function is triggered we know which rule is violated. - */ -interface Handler<T extends ts.Node> { - handlerFunction(checker: Checker, node: T): void; - code: number; -} - -/** - * Tsetse rules use on() and addFailureAtNode() for rule implementations. - * Rules can get a ts.TypeChecker from checker.typeChecker so typed rules are - * possible. Compiler uses execute() to run the Tsetse check. - */ -export class Checker { - /** Node to handlers mapping for all enabled rules. */ - private readonly nodeHandlersMap = - new Map<ts.SyntaxKind, Array<Handler<ts.Node>>>(); - /** - * Mapping from identifier name to handlers for all rules inspecting property - * names. - */ - private readonly namedIdentifierHandlersMap = - new Map<string, Array<Handler<ts.Identifier>>>(); - /** - * Mapping from property name to handlers for all rules inspecting property - * accesses expressions. - */ - private readonly namedPropertyAccessHandlersMap = - new Map<string, Array<Handler<ts.PropertyAccessExpression>>>(); - /** - * Mapping from string literal value to handlers for all rules inspecting - * string literals. - */ - private readonly stringLiteralElementAccessHandlersMap = - new Map<string, Array<Handler<ts.ElementAccessExpression>>>(); - - private failures: Failure[] = []; - private currentSourceFile: ts.SourceFile|undefined; - // currentCode will be set before invoking any handler functions so the value - // initialized here is never used. - private currentCode = 0; - /** Allow typed rules via typeChecker. */ - typeChecker: ts.TypeChecker; - - constructor(program: ts.Program) { - // Avoid the cost for each rule to create a new TypeChecker. - this.typeChecker = program.getTypeChecker(); - } - - /** - * This doesn't run any checks yet. Instead, it registers `handlerFunction` on - * `nodeKind` node in `nodeHandlersMap` map. After all rules register their - * handlers, the source file AST will be traversed. - */ - on<T extends ts.Node>( - nodeKind: T['kind'], handlerFunction: (checker: Checker, node: T) => void, - code: number) { - const newHandler: Handler<T> = {handlerFunction, code}; - const registeredHandlers = this.nodeHandlersMap.get(nodeKind); - if (registeredHandlers === undefined) { - this.nodeHandlersMap.set(nodeKind, [newHandler]); - } else { - registeredHandlers.push(newHandler); - } - } - - /** - * Similar to `on`, but registers handlers on more specific node type, i.e., - * identifiers. - */ - onNamedIdentifier( - identifierName: string, - handlerFunction: (checker: Checker, node: ts.Identifier) => void, - code: number) { - const newHandler: Handler<ts.Identifier> = {handlerFunction, code}; - const registeredHandlers = - this.namedIdentifierHandlersMap.get(identifierName); - if (registeredHandlers === undefined) { - this.namedIdentifierHandlersMap.set(identifierName, [newHandler]); - } else { - registeredHandlers.push(newHandler); - } - } - - /** - * Similar to `on`, but registers handlers on more specific node type, i.e., - * property access expressions. - */ - onNamedPropertyAccess( - propertyName: string, - handlerFunction: - (checker: Checker, node: ts.PropertyAccessExpression) => void, - code: number) { - const newHandler: - Handler<ts.PropertyAccessExpression> = {handlerFunction, code}; - const registeredHandlers = - this.namedPropertyAccessHandlersMap.get(propertyName); - if (registeredHandlers === undefined) { - this.namedPropertyAccessHandlersMap.set(propertyName, [newHandler]); - } else { - registeredHandlers.push(newHandler); - } - } - - /** - * Similar to `on`, but registers handlers on more specific node type, i.e., - * element access expressions with string literals as keys. - */ - onStringLiteralElementAccess( - key: string, - handlerFunction: - (checker: Checker, node: ts.ElementAccessExpression) => void, - code: number) { - const newHandler: - Handler<ts.ElementAccessExpression> = {handlerFunction, code}; - const registeredHandlers = - this.stringLiteralElementAccessHandlersMap.get(key); - if (registeredHandlers === undefined) { - this.stringLiteralElementAccessHandlersMap.set(key, [newHandler]); - } else { - registeredHandlers.push(newHandler); - } - } - - /** - * Add a failure with a span. - */ - addFailure(start: number, end: number, failureText: string, fix?: Fix) { - if (!this.currentSourceFile) { - throw new Error('Source file not defined'); - } - if (start >= end || end > this.currentSourceFile.end || start < 0) { - // Since only addFailureAtNode() is exposed for now this shouldn't happen. - throw new Error( - `Invalid start and end position: [${start}, ${end}]` + - ` in file ${this.currentSourceFile.fileName}.`); - } - - const failure = new Failure( - this.currentSourceFile, start, end, failureText, this.currentCode, fix); - this.failures.push(failure); - } - - addFailureAtNode(node: ts.Node, failureText: string, fix?: Fix) { - // node.getStart() takes a sourceFile as argument whereas node.getEnd() - // doesn't need it. - this.addFailure( - node.getStart(this.currentSourceFile), node.getEnd(), failureText, fix); - } - - /** Dispatch general handlers registered via `on` */ - dispatchNodeHandlers(node: ts.Node) { - const handlers = this.nodeHandlersMap.get(node.kind); - if (handlers === undefined) return; - - for (const handler of handlers) { - this.currentCode = handler.code; - handler.handlerFunction(this, node); - } - } - - /** Dispatch identifier handlers registered via `onNamedIdentifier` */ - dispatchNamedIdentifierHandlers(id: ts.Identifier) { - const handlers = this.namedIdentifierHandlersMap.get(id.text); - if (handlers === undefined) return; - - for (const handler of handlers) { - this.currentCode = handler.code; - handler.handlerFunction(this, id); - } - } - - /** - * Dispatch property access handlers registered via `onNamedPropertyAccess` - */ - dispatchNamedPropertyAccessHandlers(prop: ts.PropertyAccessExpression) { - const handlers = this.namedPropertyAccessHandlersMap.get(prop.name.text); - if (handlers === undefined) return; - - for (const handler of handlers) { - this.currentCode = handler.code; - handler.handlerFunction(this, prop); - } - } - - /** - * Dispatch string literal handlers registered via - * `onStringLiteralElementAccess`. - */ - dispatchStringLiteralElementAccessHandlers(elem: ts.ElementAccessExpression) { - const ty = this.typeChecker.getTypeAtLocation(elem.argumentExpression); - - if (!ty.isStringLiteral()) return; - - const handlers = this.stringLiteralElementAccessHandlersMap.get(ty.value); - if (handlers === undefined) return; - - for (const handler of handlers) { - this.currentCode = handler.code; - handler.handlerFunction(this, elem); - } - } - - /** - * Walk `sourceFile`, invoking registered handlers with Checker as the first - * argument and current node as the second argument. Return failures if there - * are any. - */ - execute(sourceFile: ts.SourceFile): Failure[] { - const thisChecker = this; - this.currentSourceFile = sourceFile; - this.failures = []; - run(sourceFile); - return this.failures; - - function run(node: ts.Node) { - // Dispatch handlers registered via `on` - thisChecker.dispatchNodeHandlers(node); - - // Dispatch handlers for named identifiers and properties - if (ts.isIdentifier(node)) { - thisChecker.dispatchNamedIdentifierHandlers(node); - } else if (ts.isPropertyAccessExpression(node)) { - thisChecker.dispatchNamedPropertyAccessHandlers(node); - } else if (ts.isElementAccessExpression(node)) { - thisChecker.dispatchStringLiteralElementAccessHandlers(node); - } - - ts.forEachChild(node, run); - } - } -}
diff --git a/packages/concatjs/internal/tsetse/error_code.ts b/packages/concatjs/internal/tsetse/error_code.ts deleted file mode 100644 index 7b85b71..0000000 --- a/packages/concatjs/internal/tsetse/error_code.ts +++ /dev/null
@@ -1,19 +0,0 @@ -/** - * Error codes for tsetse checks. - * - * Start with 21222 and increase linearly. - * The intent is for these codes to be fixed, so that tsetse users can - * search for them in user forums and other media. - */ -export enum ErrorCode { - CHECK_RETURN_VALUE = 21222, - EQUALS_NAN = 21223, - BAN_EXPECT_TRUTHY_PROMISE = 21224, - MUST_USE_PROMISES = 21225, - BAN_PROMISE_AS_CONDITION = 21226, - PROPERTY_RENAMING_SAFE = 21227, - CONFORMANCE_PATTERN = 21228, - BAN_MUTABLE_EXPORTS = 21229, - BAN_STRING_INITIALIZED_SETS = 21230, - MUST_TYPE_ASSERT_JSON_PARSE = 21231, -}
diff --git a/packages/concatjs/internal/tsetse/failure.ts b/packages/concatjs/internal/tsetse/failure.ts deleted file mode 100644 index 02ccd48..0000000 --- a/packages/concatjs/internal/tsetse/failure.ts +++ /dev/null
@@ -1,167 +0,0 @@ -import * as ts from 'typescript'; - -/** - * A Tsetse check Failure is almost identical to a Diagnostic from TypeScript - * except that: - * (1) The error code is defined by each individual Tsetse rule. - * (2) The optional `source` property is set to `Tsetse` so the host (VS Code - * for instance) would use that to indicate where the error comes from. - * (3) There's an optional suggestedFix field. - */ -export class Failure { - constructor( - private readonly sourceFile: ts.SourceFile, - private readonly start: number, private readonly end: number, - private readonly failureText: string, private readonly code: number, - private readonly suggestedFix?: Fix) {} - - /** - * This returns a structure compatible with ts.Diagnostic, but with added - * fields, for convenience and to support suggested fixes. - */ - toDiagnostic(): DiagnosticWithFix { - return { - file: this.sourceFile, - start: this.start, - end: this.end, // Not in ts.Diagnostic, but always useful for - // start-end-using systems. - length: this.end - this.start, - messageText: this.failureText, - category: ts.DiagnosticCategory.Error, - code: this.code, - // source is the name of the plugin. - source: 'Tsetse', - fix: this.suggestedFix - }; - } - - /** - * Same as toDiagnostic, but include the fix in the message, so that systems - * that don't support displaying suggested fixes can still surface that - * information. This assumes the diagnostic message is going to be presented - * within the context of the problematic code - */ - toDiagnosticWithStringifiedFix(): DiagnosticWithFix { - const diagnostic = this.toDiagnostic(); - if (this.suggestedFix) { - diagnostic.messageText += ' ' + this.fixToReadableStringInContext(); - } - return diagnostic; - } - - toString(): string { - return `{ sourceFile:${ - this.sourceFile ? this.sourceFile.fileName : 'unknown'}, start:${ - this.start}, end:${this.end}, fix:${fixToString(this.suggestedFix)} }`; - } - - - /** - * Stringifies a `Fix`, in a way that makes sense when presented alongside the - * finding. This is a heuristic, obviously. - */ - fixToReadableStringInContext() { - if (!this.suggestedFix) return ''; // no changes, nothing to state. - const f: Fix = this.suggestedFix; - let fixText = ''; - - for (const c of f.changes) { - // Remove leading/trailing whitespace from the stringified suggestions: - // since we add line breaks after each line of stringified suggestion, and - // since users will manually apply the fix, there is no need to show - // trailing whitespace. This is however just for stringification of the - // fixes: the suggested fix itself still keeps trailing whitespace. - const printableReplacement = c.replacement.trim(); - - // Insertion. - if (c.start === c.end) { - // Try to see if that's an import. - if (c.replacement.indexOf('import') !== -1) { - fixText += `- Add new import: ${printableReplacement}\n`; - } else { - // Insertion that's not a full import. This should rarely happen in - // our context, and we don't have a great message for these. - // For instance, this could be the addition of a new symbol in an - // existing import (`import {foo}` becoming `import {foo, bar}`). - fixText += `- Insert ${this.readableRange(c.start, c.end)}: ${ - printableReplacement}\n`; - } - } else if (c.start === this.start && c.end === this.end) { - // We assume the replacement is the main part of the fix, so put that - // individual change first in `fixText`. - fixText = `- Replace the full match with: ${printableReplacement}\n` + - fixText; - } else { - // Fallback case: Use a numerical range to specify a replacement. In - // general, falling through in this case should be avoided, as it's not - // really readable without an IDE (the range can be outside of the - // matched code). - fixText = `- Replace ${this.readableRange(c.start, c.end)} with: ` + - `${printableReplacement}\n${fixText}`; - } - } - - return 'Suggested fix:\n' + fixText.trim(); - } - - // TS indexes from 0 both ways, but tooling generally indexes from 1 for both - // lines and columns. The translation is done here. - readableRange(from: number, to: number) { - const lcf = this.sourceFile.getLineAndCharacterOfPosition(from); - const lct = this.sourceFile.getLineAndCharacterOfPosition(to); - if (lcf.line === lct.line) { - if (lcf.character === lct.character) { - return `at line ${lcf.line + 1}, char ${lcf.character + 1}`; - } - return `line ${lcf.line + 1}, from char ${lcf.character + 1} to ${ - lct.character + 1}`; - } else { - return `from line ${lcf.line + 1}, char ${lcf.character + 1} to line ${ - lct.line + 1}, char ${lct.character + 1}`; - } - } -} - -/** - * A `Fix` is a potential repair to the associated `Failure`. - */ -export interface Fix { - /** - * The individual text replacements composing that fix. - */ - changes: IndividualChange[], -} - -/** - * An individual text replacement/insertion in a source file. Used as part of a - * `Fix`. - */ -export interface IndividualChange { - sourceFile: ts.SourceFile, start: number, end: number, replacement: string -} - -/** - * A ts.Diagnostic that might include a `Fix`, and with an added `end` field for - * convenience. - */ -export interface DiagnosticWithFix extends ts.Diagnostic { - end: number; - fix?: Fix; -} - -/** - * Stringifies a `Fix`, replacing the `ts.SourceFile` with the matching - * filename. - */ -export function fixToString(f?: Fix) { - if (!f) return 'undefined'; - return '{' + JSON.stringify(f.changes.map(ic => { - return { - start: ic.start, - end: ic.end, - replacement: ic.replacement, - fileName: ic.sourceFile.fileName - }; - })) + - '}' -}
diff --git a/packages/concatjs/internal/tsetse/language_service_plugin.ts b/packages/concatjs/internal/tsetse/language_service_plugin.ts deleted file mode 100644 index 525241d..0000000 --- a/packages/concatjs/internal/tsetse/language_service_plugin.ts +++ /dev/null
@@ -1,39 +0,0 @@ -import * as ts from 'typescript/lib/tsserverlibrary'; -import * as pluginApi from '../tsc_wrapped/plugin_api'; -import {Checker} from './checker'; -import {registerRules} from './runner'; - -// Installs the Tsetse language server plugin, which checks Tsetse rules in your -// editor and shows issues as semantic errors (red squiggly underline). - -function init() { - return { - create(info: ts.server.PluginCreateInfo) { - const oldService = info.languageService; - const proxy = pluginApi.createProxy(oldService); - proxy.getSemanticDiagnostics = (fileName: string) => { - const program = oldService.getProgram(); - if (!program) { - throw new Error( - 'Failed to initialize tsetse language_service_plugin: program is undefined'); - } - - const checker = new Checker(program); - - // Add disabledRules to tsconfig to disable specific rules - // "plugins": [ - // {"name": "...", "disabledRules": ["equals-nan"]} - // ] - registerRules(checker, info.config.disabledRules || []); - const result = [...oldService.getSemanticDiagnostics(fileName)]; - // Note that this ignores suggested fixes. - result.push(...checker.execute(program.getSourceFile(fileName)!) - .map(failure => failure.toDiagnostic())); - return result; - }; - return proxy; - } - }; -} - -export = init;
diff --git a/packages/concatjs/internal/tsetse/rule.ts b/packages/concatjs/internal/tsetse/rule.ts deleted file mode 100644 index 7cc07a8..0000000 --- a/packages/concatjs/internal/tsetse/rule.ts +++ /dev/null
@@ -1,21 +0,0 @@ -import {Checker} from './checker'; - -/** - * Tsetse rules should extend AbstractRule and provide a `register` function. - * Rules are instantiated once per compilation operation and used across many - * files. - */ -export abstract class AbstractRule { - /** - * A lower-dashed-case name for that rule. This is not used by Tsetse itself, - * but the integrators might (such as the TypeScript Bazel rules, for - * instance). - */ - abstract readonly ruleName: string; - abstract readonly code: number; - - /** - * Registers handler functions on nodes in Checker. - */ - abstract register(checker: Checker): void; -}
diff --git a/packages/concatjs/internal/tsetse/rules/ban_expect_truthy_promise_rule.ts b/packages/concatjs/internal/tsetse/rules/ban_expect_truthy_promise_rule.ts deleted file mode 100644 index 6bee264..0000000 --- a/packages/concatjs/internal/tsetse/rules/ban_expect_truthy_promise_rule.ts +++ /dev/null
@@ -1,79 +0,0 @@ -/** - * @fileoverview Bans expect(returnsPromise()).toBeTruthy(). Promises are always - * truthy, and this pattern is likely to be a bug where the developer meant - * expect(await returnsPromise()).toBeTruthy() and forgot the await. - */ - -import * as tsutils from 'tsutils'; -import * as ts from 'typescript'; - -import {Checker} from '../checker'; -import {ErrorCode} from '../error_code'; -import {AbstractRule} from '../rule'; - -export class Rule extends AbstractRule { - static readonly RULE_NAME = 'ban-expect-truthy-promise'; - readonly ruleName = Rule.RULE_NAME; - readonly code = ErrorCode.BAN_EXPECT_TRUTHY_PROMISE; - - register(checker: Checker) { - checker.on( - ts.SyntaxKind.PropertyAccessExpression, checkForTruthy, this.code); - } -} - -function checkForTruthy(checker: Checker, node: ts.PropertyAccessExpression) { - if (node.name.text !== 'toBeTruthy') { - return; - } - - const expectCallNode = getLeftmostNode(node); - if (!ts.isCallExpression(expectCallNode)) { - return; - } - - if (!ts.isIdentifier(expectCallNode.expression) || expectCallNode.expression.text !== 'expect') { - return; - } - - if (expectCallNode.arguments.length === 0 || ts.isAwaitExpression(expectCallNode.arguments[0])) { - return; - } - - const tc = checker.typeChecker; - - const signature = tc.getResolvedSignature(expectCallNode); - if (signature === undefined) { - return; - } - - const symbol = tc.getReturnTypeOfSignature(signature).getSymbol(); - if (symbol === undefined) { - return; - } - - // Only look for methods named expect that return a Matchers - if (symbol.name !== 'Matchers') { - return; - } - - const argType = tc.getTypeAtLocation(expectCallNode.arguments[0]); - if (!tsutils.isThenableType(tc, expectCallNode.arguments[0], argType)) { - return; - } - - checker.addFailureAtNode( - node, - `Value passed to expect() is of type ${tc.typeToString(argType)}, which` + - ` is thenable. Promises are always truthy. Either use toBe(true) or` + - ` await the value.` + - `\n\tSee http://tsetse.info/ban-expect-truthy-promise`); -} - -function getLeftmostNode(node: ts.PropertyAccessExpression) { - let current: ts.LeftHandSideExpression|undefined = node; - while (ts.isPropertyAccessExpression(current)) { - current = current.expression; - } - return current; -}
diff --git a/packages/concatjs/internal/tsetse/rules/ban_mutable_exports_rule.ts b/packages/concatjs/internal/tsetse/rules/ban_mutable_exports_rule.ts deleted file mode 100644 index da852c6..0000000 --- a/packages/concatjs/internal/tsetse/rules/ban_mutable_exports_rule.ts +++ /dev/null
@@ -1,67 +0,0 @@ -/** - * @fileoverview Bans 'export' of mutable variables. - * It is illegal to mutate them, so you might as well use 'const'. - */ - -import * as ts from 'typescript'; - -import {Checker} from '../checker'; -import {ErrorCode} from '../error_code'; -import {AbstractRule} from '../rule'; - -export class Rule extends AbstractRule { - static readonly RULE_NAME = 'ban-mutable-exports'; - readonly ruleName = Rule.RULE_NAME; - readonly code = ErrorCode.BAN_MUTABLE_EXPORTS; - - register(checker: Checker) { - // Strategy: take all the exports of the file, then look at whether - // they're const or not. This is simpler than the alternative of - // trying to match all the various kinds of 'export' syntax, such - // as 'export var ...', 'export {...}', 'export default ...'. - checker.on(ts.SyntaxKind.SourceFile, checkFile, this.code); - } -} - -function checkFile(checker: Checker, file: ts.SourceFile) { - // Allow in d.ts files, which are modelling external JS that doesn't - // follow our rules. - if (file.fileName.endsWith('.d.ts')) { - return; - } - const sym = checker.typeChecker.getSymbolAtLocation(file); - if (!sym) return; - const exports = checker.typeChecker.getExportsOfModule(sym); - for (const exp of exports) { - // In the case of - // let x = 3; export {x}; - // The exported symbol is the latter x, but we must dealias to - // the former to judge whether it's const or not. - let sym = exp; - if (sym.flags & ts.SymbolFlags.Alias) { - sym = checker.typeChecker.getAliasedSymbol(exp); - } - const decl = sym.valueDeclaration; - if (!decl) continue; // Skip e.g. type declarations. - - if (decl.getSourceFile() !== file) { - // Reexports are best warned about in the original file - continue; - } - - if (!ts.isVariableDeclaration(decl) && !ts.isBindingElement(decl)) { - // Skip e.g. class declarations. - continue; - } - - const isConst = (ts.getCombinedNodeFlags(decl) & ts.NodeFlags.Const) !== 0; - if (!isConst && exp.declarations !== undefined) { - // Note: show the failure at the exported symbol's declaration site, - // not the dealiased 'sym', so that the error message shows at the - // 'export' statement and not the variable declaration. - checker.addFailureAtNode( - exp.declarations[0], - `Exports must be const.`); - } - } -}
diff --git a/packages/concatjs/internal/tsetse/rules/ban_promise_as_condition_rule.ts b/packages/concatjs/internal/tsetse/rules/ban_promise_as_condition_rule.ts deleted file mode 100644 index a9da23e..0000000 --- a/packages/concatjs/internal/tsetse/rules/ban_promise_as_condition_rule.ts +++ /dev/null
@@ -1,144 +0,0 @@ -/** - * @fileoverview Bans using a promise as a condition. Promises are always - * truthy, and this pattern is likely to be a bug where the developer meant - * if(await returnsPromise()) {} and forgot the await. - */ - -import * as tsutils from 'tsutils'; -import * as ts from 'typescript'; - -import {Checker} from '../checker'; -import {ErrorCode} from '../error_code'; -import {AbstractRule} from '../rule'; - -export class Rule extends AbstractRule { - static readonly RULE_NAME = 'ban-promise-as-condition'; - readonly ruleName = Rule.RULE_NAME; - readonly code = ErrorCode.BAN_PROMISE_AS_CONDITION; - - register(checker: Checker) { - checker.on( - ts.SyntaxKind.ConditionalExpression, checkConditional, this.code); - checker.on( - ts.SyntaxKind.BinaryExpression, checkBinaryExpression, this.code); - checker.on(ts.SyntaxKind.WhileStatement, checkWhileStatement, this.code); - checker.on(ts.SyntaxKind.IfStatement, checkIfStatement, this.code); - } -} - -/** Error message to display. */ -function thenableText(nodeType: string, isVariable: boolean) { - return `Found a thenable ${isVariable ? 'variable' : 'return value'} being` + - ` used as ${ - nodeType}. Promises are always truthy, await the value to get` + - ' a boolean value.'; -} - -function thenableVariableText(nodeType: string) { - return thenableText(nodeType, true); -} - -function thenableReturnText(nodeType: string) { - return thenableText(nodeType, false); -} - -/** Ternary: prom ? y : z */ -function checkConditional(checker: Checker, node: ts.ConditionalExpression) { - addFailureIfThenableCallExpression( - checker, node.condition, thenableReturnText('a conditional')); - - addFailureIfThenableIdentifier( - checker, node.condition, thenableVariableText('a conditional')); -} - -/** - * Binary expression: prom || y or prom && y. Only check left side because - * myThing && myThing.prom seems legitimate. - */ -function checkBinaryExpression(checker: Checker, node: ts.BinaryExpression) { - if (node.operatorToken.kind !== ts.SyntaxKind.BarBarToken && - node.operatorToken.kind !== ts.SyntaxKind.AmpersandAmpersandToken) { - return; - } - - addFailureIfThenableCallExpression( - checker, node.left, thenableReturnText('a binary expression')); - - addFailureIfThenableIdentifier( - checker, node.left, thenableVariableText('a binary expression')); -} - -/** While statement: while (prom) {} */ -function checkWhileStatement(checker: Checker, node: ts.WhileStatement) { - addFailureIfThenableCallExpression( - checker, node.expression, thenableReturnText('a while statement')); - - addFailureIfThenableIdentifier( - checker, node.expression, thenableVariableText('a while statement')); -} - -/** If statement: if (prom) {} */ -function checkIfStatement(checker: Checker, node: ts.IfStatement) { - addFailureIfThenableCallExpression( - checker, node.expression, thenableReturnText('an if statement')); - - addFailureIfThenableIdentifier( - checker, node.expression, thenableVariableText('an if statement')); -} - -/** Helper methods */ - -function addFailureIfThenableCallExpression( - checker: Checker, callExpression: ts.Expression, errorMessage: string) { - if (!tsutils.isCallExpression(callExpression)) { - return; - } - - const typeChecker = checker.typeChecker; - const signature = typeChecker.getResolvedSignature(callExpression); - - // Return value of getResolvedSignature is `Signature | undefined` in ts 3.1 - // so we must check if the return value is valid to compile with ts 3.1. - if (!signature) { - throw new Error('Unexpected undefined signature for call expression'); - } - - const returnType = typeChecker.getReturnTypeOfSignature(signature); - - if (isNonFalsyThenableType(typeChecker, callExpression, returnType)) { - checker.addFailureAtNode(callExpression, errorMessage); - } -} - -function addFailureIfThenableIdentifier( - checker: Checker, identifier: ts.Expression, errorMessage: string) { - if (!tsutils.isIdentifier(identifier)) { - return; - } - - if (isNonFalsyThenableType(checker.typeChecker, identifier)) { - checker.addFailureAtNode(identifier, errorMessage); - } -} - -/** - * If the type is a union type and has a falsy part it may be legitimate to use - * it as a condition, so allow those through. (e.g. Promise<boolean> | boolean) - * Otherwise, check if it's thenable. If so it should be awaited. - */ -function isNonFalsyThenableType( - typeChecker: ts.TypeChecker, node: ts.Expression, - type = typeChecker.getTypeAtLocation(node)) { - if (hasFalsyParts(typeChecker.getTypeAtLocation(node))) { - return false; - } - - return tsutils.isThenableType(typeChecker, node, type); -} - -function hasFalsyParts(type: ts.Type) { - const typeParts = tsutils.unionTypeParts(type); - const hasFalsyParts = - typeParts.filter((part) => tsutils.isFalsyType(part)).length > 0; - return hasFalsyParts; -}
diff --git a/packages/concatjs/internal/tsetse/rules/ban_string_initialized_sets_rule.ts b/packages/concatjs/internal/tsetse/rules/ban_string_initialized_sets_rule.ts deleted file mode 100644 index 22d660d..0000000 --- a/packages/concatjs/internal/tsetse/rules/ban_string_initialized_sets_rule.ts +++ /dev/null
@@ -1,65 +0,0 @@ -/** - * @fileoverview Bans `new Set(<string>)` since it is a potential source of bugs - * due to strings also implementing `Iterable<string>`. - */ - -import * as ts from 'typescript'; - -import {Checker} from '../checker'; -import {ErrorCode} from '../error_code'; -import {AbstractRule} from '../rule'; - -const errorMsg = 'Value passed to Set constructor is a string. This will' + - ' create a Set of the characters of the string, rather than a Set' + - ' containing the string. To make a Set of the string, pass an array' + - ' containing the string. To make a Set of the characters, use \'as\' to ' + - ' create an Iterable<string>, eg: new Set(myStr as Iterable<string>).'; - -export class Rule extends AbstractRule { - static readonly RULE_NAME = 'ban-string-initialized-sets'; - readonly ruleName = Rule.RULE_NAME; - readonly code = ErrorCode.BAN_STRING_INITIALIZED_SETS; - - register(checker: Checker) { - checker.on(ts.SyntaxKind.NewExpression, checkNewExpression, this.code); - } -} - -function checkNewExpression(checker: Checker, node: ts.NewExpression) { - const typeChecker = checker.typeChecker; - - // Check that it's a Set which is being constructed - const ctorTypeSymbol = - typeChecker.getTypeAtLocation(node.expression).getSymbol(); - - if (!ctorTypeSymbol || ctorTypeSymbol.declarations === undefined || - ctorTypeSymbol.getEscapedName() !== 'SetConstructor') { - return; - } - const isES2015SetCtor = ctorTypeSymbol.declarations.some((decl) => { - return sourceFileIsStdLib(decl.getSourceFile()); - }); - if (!isES2015SetCtor) return; - - // If there's no arguments provided, then it's not a string so bail out. - if (!node.arguments || node.arguments.length !== 1) return; - - // Check the type of the first argument, expanding union & intersection types - const arg = node.arguments[0]; - const argType = typeChecker.getTypeAtLocation(arg); - const allTypes = argType.isUnionOrIntersection() ? argType.types : [argType]; - - // Checks if the type (or any of the union/intersection types) are either - // strings or string literals. - const typeContainsString = allTypes.some((tsType) => { - return (tsType.getFlags() & ts.TypeFlags.StringLike) !== 0; - }); - - if (!typeContainsString) return; - - checker.addFailureAtNode(arg, errorMsg); -} - -function sourceFileIsStdLib(sourceFile: ts.SourceFile) { - return /lib\.es2015\.(collection|iterable)\.d\.ts$/.test(sourceFile.fileName); -}
diff --git a/packages/concatjs/internal/tsetse/rules/check_return_value_rule.ts b/packages/concatjs/internal/tsetse/rules/check_return_value_rule.ts deleted file mode 100644 index 35e6b19..0000000 --- a/packages/concatjs/internal/tsetse/rules/check_return_value_rule.ts +++ /dev/null
@@ -1,164 +0,0 @@ -/** - * @fileoverview A Tsetse rule that checks the return value of certain functions - * must be used. - */ - -import * as tsutils from 'tsutils'; -import * as ts from 'typescript'; - -import {Checker} from '../checker'; -import {ErrorCode} from '../error_code'; -import {AbstractRule} from '../rule'; - -const FAILURE_STRING = 'return value is unused.' + - '\n\tSee http://tsetse.info/check-return-value'; - -// A list of well-known functions that the return value must be used. If unused -// then the function call is either a no-op (e.g. 'foo.trim()' foo is unchanged) -// or can be replaced by another (Array.map() should be replaced with a loop or -// Array.forEach() if the return value is unused). -const METHODS_TO_CHECK = new Set<string>([ - ['Array', 'concat'], - ['Array', 'filter'], - ['Array', 'map'], - ['Array', 'slice'], - ['Function', 'bind'], - ['Object', 'create'], - ['string', 'concat'], - ['string', 'normalize'], - ['string', 'padStart'], - ['string', 'padEnd'], - ['string', 'repeat'], - ['string', 'slice'], - ['string', 'split'], - ['string', 'substr'], - ['string', 'substring'], - ['string', 'toLocaleLowerCase'], - ['string', 'toLocaleUpperCase'], - ['string', 'toLowerCase'], - ['string', 'toUpperCase'], - ['string', 'trim'], -].map(list => list.join('#'))); - -/** A rule to ensure required return values from common functions are used. */ -export class Rule extends AbstractRule { - static readonly RULE_NAME = 'check-return-value'; - readonly ruleName = Rule.RULE_NAME; - readonly code = ErrorCode.CHECK_RETURN_VALUE; - - // registers checkCallExpression() function on ts.CallExpression node. - // TypeScript conformance will traverse the AST of each source file and run - // checkCallExpression() every time it encounters a ts.CallExpression node. - register(checker: Checker) { - checker.on(ts.SyntaxKind.CallExpression, checkCallExpression, this.code); - } -} - -function checkCallExpression(checker: Checker, node: ts.CallExpression) { - // Short-circuit before using the typechecker if possible, as its expensive. - // Workaround for https://github.com/Microsoft/TypeScript/issues/27997 - if (tsutils.isExpressionValueUsed(node)) { - return; - } - - // Check if this CallExpression is one of the well-known functions and returns - // a non-void value that is unused. - const signature = checker.typeChecker.getResolvedSignature(node); - if (signature !== undefined) { - const returnType = checker.typeChecker.getReturnTypeOfSignature(signature); - if (!!(returnType.flags & ts.TypeFlags.Void)) { - return; - } - // Although hasCheckReturnValueJsDoc() is faster than isBlackListed(), it - // returns false most of the time and thus isBlackListed() would have to run - // anyway. Therefore we short-circuit hasCheckReturnValueJsDoc(). - if (!isBlackListed(node, checker.typeChecker) && - !hasCheckReturnValueJsDoc(node, checker.typeChecker)) { - return; - } - - checker.addFailureAtNode(node, FAILURE_STRING); - } -} - -function isBlackListed(node: ts.CallExpression, tc: ts.TypeChecker): boolean { - type AccessExpression = - ts.PropertyAccessExpression|ts.ElementAccessExpression; - switch (node.expression.kind) { - case ts.SyntaxKind.PropertyAccessExpression: - case ts.SyntaxKind.ElementAccessExpression: - // Example: foo.bar() or foo[bar]() - // expressionNode is foo - const nodeExpression = (node.expression as AccessExpression).expression; - const nodeExpressionString = nodeExpression.getText(); - const nodeType = tc.getTypeAtLocation(nodeExpression); - - // nodeTypeString is the string representation of the type of foo - let nodeTypeString = tc.typeToString(nodeType); - if (nodeTypeString.endsWith('[]')) { - nodeTypeString = 'Array'; - } - if (nodeTypeString === 'ObjectConstructor') { - nodeTypeString = 'Object'; - } - if (tsutils.isTypeFlagSet(nodeType, ts.TypeFlags.StringLiteral)) { - nodeTypeString = 'string'; - } - - // nodeFunction is bar - let nodeFunction = ''; - if (tsutils.isPropertyAccessExpression(node.expression)) { - nodeFunction = node.expression.name.getText(); - } - if (tsutils.isElementAccessExpression(node.expression)) { - const argument = node.expression.argumentExpression; - if (argument !== undefined) { - nodeFunction = argument.getText(); - } - } - - // Check if 'foo#bar' or `${typeof foo}#bar` is in the blacklist. - if (METHODS_TO_CHECK.has(`${nodeTypeString}#${nodeFunction}`) || - METHODS_TO_CHECK.has(`${nodeExpressionString}#${nodeFunction}`)) { - return true; - } - - // For 'str.replace(regexp|substr, newSubstr|function)' only check when - // the second parameter is 'newSubstr'. - if ((`${nodeTypeString}#${nodeFunction}` === 'string#replace') || - (`${nodeExpressionString}#${nodeFunction}` === 'string#replace')) { - return node.arguments.length === 2 && - !tsutils.isFunctionWithBody(node.arguments[1]); - } - break; - case ts.SyntaxKind.Identifier: - // Example: foo() - // We currently don't have functions of this kind in blacklist. - const identifier = node.expression as ts.Identifier; - if (METHODS_TO_CHECK.has(identifier.text)) { - return true; - } - break; - default: - break; - } - return false; -} - -function hasCheckReturnValueJsDoc(node: ts.CallExpression, tc: ts.TypeChecker) { - let symbol = tc.getSymbolAtLocation(node.expression); - if (symbol === undefined) { - return false; - } - - if (tsutils.isSymbolFlagSet(symbol, ts.SymbolFlags.Alias)) { - symbol = tc.getAliasedSymbol(symbol); - } - - for (const jsDocTagInfo of symbol.getJsDocTags()) { - if (jsDocTagInfo.name === 'checkReturnValue') { - return true; - } - } - return false; -}
diff --git a/packages/concatjs/internal/tsetse/rules/conformance_pattern_rule.ts b/packages/concatjs/internal/tsetse/rules/conformance_pattern_rule.ts deleted file mode 100644 index d620b87..0000000 --- a/packages/concatjs/internal/tsetse/rules/conformance_pattern_rule.ts +++ /dev/null
@@ -1,60 +0,0 @@ -import {Checker} from '../checker'; -import {ErrorCode} from '../error_code'; -import {AbstractRule} from '../rule'; -import {Fixer} from '../util/fixer'; -import {PatternKind, PatternRuleConfig} from '../util/pattern_config'; -import {NameEngine} from '../util/pattern_engines/name_engine'; -import {PatternEngine} from '../util/pattern_engines/pattern_engine'; -import {PropertyEngine} from '../util/pattern_engines/property_engine'; -import {PropertyNonConstantWriteEngine} from '../util/pattern_engines/property_non_constant_write_engine'; -import {PropertyWriteEngine} from '../util/pattern_engines/property_write_engine'; - - -/** - * Builds a Rule that matches a certain pattern, given as parameter, and - * that can additionally run a suggested fix generator on the matches. - * - * This is templated, mostly to ensure the nodes that have been matched - * correspond to what the Fixer expects. - */ -export class ConformancePatternRule implements AbstractRule { - readonly ruleName: string; - readonly code: number; - private readonly engine: PatternEngine; - - constructor(config: PatternRuleConfig, fixer?: Fixer) { - this.code = config.errorCode; - // Avoid empty rule names. - this.ruleName = config.name || `conformance-pattern-${config.kind}`; - - switch (config.kind) { - case PatternKind.BANNED_PROPERTY: - this.engine = new PropertyEngine(config, fixer); - break; - case PatternKind.BANNED_PROPERTY_WRITE: - this.engine = new PropertyWriteEngine(config, fixer); - break; - case PatternKind.BANNED_PROPERTY_NON_CONSTANT_WRITE: - this.engine = new PropertyNonConstantWriteEngine(config, fixer); - break; - case PatternKind.BANNED_NAME: - this.engine = new NameEngine(config, fixer); - break; - default: - throw new Error('Config type not recognized, or not implemented yet.'); - } - } - - register(checker: Checker) { - this.engine.register(checker); - } -} - -// Re-exported for convenience when instantiating rules. -/** - * The list of supported patterns useable in ConformancePatternRule. The - * patterns whose name match JSConformance patterns should behave similarly (see - * https://github.com/google/closure-compiler/wiki/JS-Conformance-Framework). - */ -export {PatternKind}; -export {ErrorCode};
diff --git a/packages/concatjs/internal/tsetse/rules/conformance_pattern_rule_test.ts b/packages/concatjs/internal/tsetse/rules/conformance_pattern_rule_test.ts deleted file mode 100644 index 1550812..0000000 --- a/packages/concatjs/internal/tsetse/rules/conformance_pattern_rule_test.ts +++ /dev/null
@@ -1,30 +0,0 @@ -import 'jasmine'; - -import {customMatchers} from '../util/testing/test_support'; -import {ConformancePatternRule, ErrorCode, PatternKind} from './conformance_pattern_rule'; - -describe('ConformancePatternRule creation', () => { - describe('naming', () => { - const baseConfig = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'do not cite', - kind: PatternKind.BANNED_PROPERTY_WRITE, - values: ['HTMLQuoteElement.prototype.cite'], - }; - - it('generates a name by default', () => { - const rule = new ConformancePatternRule(baseConfig); - expect(rule.ruleName).toBe('conformance-pattern-banned-property-write'); - }); - - it('accepts given names', () => { - const namedConfig = {name: 'myRuleName', ...baseConfig}; - const rule = new ConformancePatternRule(namedConfig); - expect(rule.ruleName).toBe('myRuleName'); - }); - }); -}); - -beforeEach(() => { - jasmine.addMatchers(customMatchers); -});
diff --git a/packages/concatjs/internal/tsetse/rules/equals_nan_rule.ts b/packages/concatjs/internal/tsetse/rules/equals_nan_rule.ts deleted file mode 100644 index ea6d8e8..0000000 --- a/packages/concatjs/internal/tsetse/rules/equals_nan_rule.ts +++ /dev/null
@@ -1,61 +0,0 @@ -/** - * @fileoverview Bans `== NaN`, `=== NaN`, `!= NaN`, and `!== NaN` in TypeScript - * code, since no value (including NaN) is equal to NaN. - */ - -import * as ts from 'typescript'; - -import {Checker} from '../checker'; -import {ErrorCode} from '../error_code'; -import {AbstractRule} from '../rule'; - -export class Rule extends AbstractRule { - static readonly RULE_NAME = 'equals-nan'; - readonly ruleName = Rule.RULE_NAME; - readonly code = ErrorCode.EQUALS_NAN; - - register(checker: Checker) { - checker.on( - ts.SyntaxKind.BinaryExpression, checkBinaryExpression, this.code); - } -} - -function checkBinaryExpression(checker: Checker, node: ts.BinaryExpression) { - const isLeftNaN = ts.isIdentifier(node.left) && node.left.text === 'NaN'; - const isRightNaN = ts.isIdentifier(node.right) && node.right.text === 'NaN'; - if (!isLeftNaN && !isRightNaN) { - return; - } - - // We avoid calling getText() on the node.operatorToken because it's slow. - // Instead, manually map back from the kind to the string form of the operator - switch (node.operatorToken.kind) { - case ts.SyntaxKind.EqualsEqualsToken: - checker.addFailureAtNode( - node, - `x == NaN is always false; use isNaN(x) instead`, - ); - break; - case ts.SyntaxKind.EqualsEqualsEqualsToken: - checker.addFailureAtNode( - node, - `x === NaN is always false; use isNaN(x) instead`, - ); - break; - case ts.SyntaxKind.ExclamationEqualsToken: - checker.addFailureAtNode( - node, - `x != NaN is always true; use !isNaN(x) instead`, - ); - break; - case ts.SyntaxKind.ExclamationEqualsEqualsToken: - checker.addFailureAtNode( - node, - `x !== NaN is always true; use !isNaN(x) instead`, - ); - break; - default: - // We don't care about other operators acting on NaN - break; - } -}
diff --git a/packages/concatjs/internal/tsetse/rules/must_type_assert_json_parse_rule.ts b/packages/concatjs/internal/tsetse/rules/must_type_assert_json_parse_rule.ts deleted file mode 100644 index 45ee772..0000000 --- a/packages/concatjs/internal/tsetse/rules/must_type_assert_json_parse_rule.ts +++ /dev/null
@@ -1,71 +0,0 @@ -/** - * @fileoverview Bans `JSON.parse(...)` that is not wrapped in a type assertion. - * See http://tsetse.info/must-type-assert-json-parse - */ - - -import * as ts from 'typescript'; - -import {Checker} from '../checker'; -import {ErrorCode} from '../error_code'; -import {AbstractRule} from '../rule'; - -const FAILURE_STRING = - 'type assert `JSON.parse() as SomeExplicitType` for type & optimization safety.\n\t' + - 'See http://tsetse.info/must-type-assert-json-parse.'; - -/** - * Ensures that all calls to JSON.parse are wrapped in an `as` expression. - */ -export class Rule extends AbstractRule { - static readonly RULE_NAME = 'must-type-assert-json-parse'; - readonly ruleName = Rule.RULE_NAME; - readonly code = ErrorCode.MUST_TYPE_ASSERT_JSON_PARSE; - - register(checker: Checker) { - checker.on(ts.SyntaxKind.CallExpression, checkCallExpression, this.code); - } -} - -/** - * Checks whether a given `parse` symbol is the JSON.parse symbol declared in - * lib.es5.d.ts. - */ -function isEcmascriptJsonParse( - checker: Checker, node: ts.LeftHandSideExpression): boolean { - const parseSymbol = checker.typeChecker.getSymbolAtLocation(node); - if (parseSymbol === undefined) return false; - - const declaration = parseSymbol.valueDeclaration; - if (declaration === undefined) return false; - - const fileName = declaration.getSourceFile().fileName; - if (!fileName.includes('typescript')) return false; - if (!fileName.endsWith('lib.es5.d.ts')) return false; - - // Narrow the node type so we can get `name`. - if (!ts.isMethodSignature(declaration)) return false; - if (declaration.name.getText() !== 'parse') return false; - - if (!ts.isInterfaceDeclaration(declaration.parent)) return false; - if (declaration.parent.name.getText() !== 'JSON') return false; - - return true; -} - -function checkCallExpression(checker: Checker, node: ts.CallExpression) { - const funcexpr = node.expression; - if (!ts.isPropertyAccessExpression(funcexpr)) { - return; - } - - // We're inside a type assert expression (JSON.parse as SomeExplicitType) so - // this is an acceptable use. Don't add a failure. - if (node.parent != null && node.parent.kind === ts.SyntaxKind.AsExpression) { - return; - } - - if (!isEcmascriptJsonParse(checker, funcexpr)) return; - - checker.addFailureAtNode(node, FAILURE_STRING); -}
diff --git a/packages/concatjs/internal/tsetse/rules/must_use_promises_rule.ts b/packages/concatjs/internal/tsetse/rules/must_use_promises_rule.ts deleted file mode 100644 index 293ea4b..0000000 --- a/packages/concatjs/internal/tsetse/rules/must_use_promises_rule.ts +++ /dev/null
@@ -1,60 +0,0 @@ -/** - * @fileoverview A Tsetse rule that checks that all promises in async function - * blocks are awaited or used. - */ - -import * as tsutils from 'tsutils'; -import * as ts from 'typescript'; - -import {Checker} from '../checker'; -import {ErrorCode} from '../error_code'; -import {AbstractRule} from '../rule'; - -const FAILURE_STRING = - 'All Promises in async functions must either be awaited or used in an expression.' + - '\n\tSee http://tsetse.info/must-use-promises'; - -/** A rule to ensure promises in async functions are awaited or used. */ -export class Rule extends AbstractRule { - static readonly RULE_NAME = 'must-use-promises'; - readonly ruleName = Rule.RULE_NAME; - readonly code = ErrorCode.MUST_USE_PROMISES; - - register(checker: Checker) { - checker.on(ts.SyntaxKind.CallExpression, checkCallExpression, this.code); - } -} - -function checkCallExpression(checker: Checker, node: ts.CallExpression) { - // Short-circuit before using the typechecker if possible, as its expensive. - // Workaround for https://github.com/Microsoft/TypeScript/issues/27997 - if (tsutils.isExpressionValueUsed(node) || !inAsyncFunction(node)) { - return; - } - - if (tsutils.isThenableType(checker.typeChecker, node)) { - checker.addFailureAtNode(node, FAILURE_STRING); - } -} - -function inAsyncFunction(node: ts.Node): boolean { - for (let inode = node.parent; inode !== undefined; inode = inode.parent) { - switch (inode.kind) { - case ts.SyntaxKind.ArrowFunction: - case ts.SyntaxKind.FunctionDeclaration: - case ts.SyntaxKind.FunctionExpression: - case ts.SyntaxKind.MethodDeclaration: - // Potentially async - return tsutils.hasModifier(inode.modifiers, ts.SyntaxKind.AsyncKeyword); - case ts.SyntaxKind.GetAccessor: - case ts.SyntaxKind.SetAccessor: - // These cannot be async - return false; - default: - // Loop and check parent - break; - } - } - - return false; -}
diff --git a/packages/concatjs/internal/tsetse/rules/property_renaming_safe.ts b/packages/concatjs/internal/tsetse/rules/property_renaming_safe.ts deleted file mode 100644 index ce8b95d..0000000 --- a/packages/concatjs/internal/tsetse/rules/property_renaming_safe.ts +++ /dev/null
@@ -1,55 +0,0 @@ - -import * as ts from 'typescript'; - -import {Checker} from '../checker'; -import {ErrorCode} from '../error_code'; -import {AbstractRule} from '../rule'; - -/** - * A Tsetse rule that checks for some potential unsafe property renaming - * patterns. - * - * Note: This rule can have false positives. - */ -export class Rule extends AbstractRule { - static readonly RULE_NAME = 'property-renaming-safe'; - readonly ruleName = Rule.RULE_NAME; - readonly code = ErrorCode.PROPERTY_RENAMING_SAFE; - - register(checker: Checker) { - checker.on( - ts.SyntaxKind.PropertyAccessExpression, - checkIndexSignAccessedWithPropAccess, this.code); - } -} - -// Copied from tsickle/src/quoting_transformer.ts, with the intention of -// removing it from there and only keeping a tsetse rule about this. -function checkIndexSignAccessedWithPropAccess( - checker: Checker, pae: ts.PropertyAccessExpression) { - // Reject dotted accesses to types that have an index type declared to quoted - // accesses, to avoid Closure renaming one access but not the other. This can - // happen because TS allows dotted access to string index types. - const typeChecker = checker.typeChecker; - const t = typeChecker.getTypeAtLocation(pae.expression); - if (!t.getStringIndexType()) return; - // Types can have string index signatures and declared properties (of the - // matching type). These properties have a symbol, as opposed to pure string - // index types. - const propSym = typeChecker.getSymbolAtLocation(pae.name); - // The decision to return below is a judgement call. Presumably, in most - // situations, dotted access to a property is correct, and should not be - // turned into quoted access even if there is a string index on the type. - // However it is possible to construct programs where this is incorrect, e.g. - // where user code assigns into a property through the index access in another - // location. - if (propSym) return; - - checker.addFailureAtNode( - pae.name, - `Property ${pae.name.text} is not declared on Type ` + - `${typeChecker.typeToString(t)}. The type has a string index ` + - `signature, but it is being accessed using a dotted property ` + - `access. -See http://tsetse.info/property-renaming-safe.`); -}
diff --git a/packages/concatjs/internal/tsetse/runner.ts b/packages/concatjs/internal/tsetse/runner.ts deleted file mode 100644 index cbe85cd..0000000 --- a/packages/concatjs/internal/tsetse/runner.ts +++ /dev/null
@@ -1,62 +0,0 @@ -/** - * @fileoverview Runner is the entry point of running Tsetse checks in compiler. - */ - -import * as ts from 'typescript'; - -import * as perfTrace from '../tsc_wrapped/perf_trace'; -import * as pluginApi from '../tsc_wrapped/plugin_api'; - -import {Checker} from './checker'; -import {AbstractRule} from './rule'; -import {Rule as BanExpectTruthyPromiseRule} from './rules/ban_expect_truthy_promise_rule'; -import {Rule as BanPromiseAsConditionRule} from './rules/ban_promise_as_condition_rule'; -import {Rule as BanStringInitializedSetsRule} from './rules/ban_string_initialized_sets_rule'; -import {Rule as CheckReturnValueRule} from './rules/check_return_value_rule'; -import {Rule as EqualsNanRule} from './rules/equals_nan_rule'; -import {Rule as MustTypeAssertJsonParseRule} from './rules/must_type_assert_json_parse_rule'; -import {Rule as MustUsePromisesRule} from './rules/must_use_promises_rule'; - -/** - * List of Tsetse rules. Shared between the program plugin and the language - * service plugin. - */ -const ENABLED_RULES: AbstractRule[] = [ - new CheckReturnValueRule(), - new EqualsNanRule(), - new BanExpectTruthyPromiseRule(), - new MustUsePromisesRule(), - new BanPromiseAsConditionRule(), - new BanStringInitializedSetsRule(), - new MustTypeAssertJsonParseRule(), -]; - -/** - * The Tsetse check plugin performs compile-time static analysis for TypeScript - * code. - */ -export class Plugin implements pluginApi.DiagnosticPlugin { - readonly name = 'tsetse'; - private readonly checker: Checker; - constructor(program: ts.Program, disabledTsetseRules: string[] = []) { - this.checker = new Checker(program); - registerRules(this.checker, disabledTsetseRules); - } - - getDiagnostics(sourceFile: ts.SourceFile) { - // Tsetse, in its plugin form, outputs ts.Diagnostic that don't make use - // of the potential suggested fixes Tsetse generates. These diagnostics are - // however displayed in context: we can therefore stringify any potential - // suggested fixes in the error message, so they don't go to waste. - return this.checker.execute(sourceFile) - .map(failure => failure.toDiagnosticWithStringifiedFix()); - } -} - -export function registerRules(checker: Checker, disabledTsetseRules: string[]) { - for (const rule of ENABLED_RULES) { - if (disabledTsetseRules.indexOf(rule.ruleName) === -1) { - rule.register(checker); - } - } -}
diff --git a/packages/concatjs/internal/tsetse/tests/ban_expect_truthy_promise/BUILD b/packages/concatjs/internal/tsetse/tests/ban_expect_truthy_promise/BUILD deleted file mode 100644 index 84fe023..0000000 --- a/packages/concatjs/internal/tsetse/tests/ban_expect_truthy_promise/BUILD +++ /dev/null
@@ -1,60 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -load("//packages/concatjs:index.bzl", "ts_library") - -licenses(["notice"]) # Apache 2.0 - -error_message = "error TS21224: Value passed.*" - -ts_library( - name = "jasmine", - testonly = 1, - srcs = [ - "jasmine_types.ts", - ], -) - -ts_library( - name = "positives", - testonly = 1, - srcs = [ - "positives.ts", - ], - expected_diagnostics = [ - "\\(31,5\\).*" + error_message, - "\\(32,5\\).*" + error_message, - "\\(33,5\\).*" + error_message, - "\\(34,5\\).*" + error_message, - "\\(35,5\\).*" + error_message, - "\\(36,5\\).*" + error_message, - "\\(37,5\\).*" + error_message, - ], - tsconfig = "//packages/concatjs/internal:tsetse/tsconfig.json", - deps = [ - ":jasmine", - ], -) - -ts_library( - name = "negatives", - testonly = 1, - srcs = [ - "negatives.ts", - ], - tsconfig = "//packages/concatjs/internal:tsetse/tsconfig.json", - deps = [ - ":jasmine", - ], -)
diff --git a/packages/concatjs/internal/tsetse/tests/ban_expect_truthy_promise/jasmine_types.ts b/packages/concatjs/internal/tsetse/tests/ban_expect_truthy_promise/jasmine_types.ts deleted file mode 100644 index bcea24f..0000000 --- a/packages/concatjs/internal/tsetse/tests/ban_expect_truthy_promise/jasmine_types.ts +++ /dev/null
@@ -1,14 +0,0 @@ -// Abbreviated Jasmine types for testing purposes - -declare function describe( - description: string, specDefinitions: () => void): void; -declare function it( - expectation: string, assertion?: (done: Function) => void, - timeout?: number): void; -declare function expect(actual: any): Matchers; - -interface Matchers { - toBeTruthy(expectationFailOutput?: any): boolean; - toBeFalsy(expectationFailOutput?: any): boolean; - not: Matchers; -}
diff --git a/packages/concatjs/internal/tsetse/tests/ban_expect_truthy_promise/negatives.ts b/packages/concatjs/internal/tsetse/tests/ban_expect_truthy_promise/negatives.ts deleted file mode 100644 index 62a3485..0000000 --- a/packages/concatjs/internal/tsetse/tests/ban_expect_truthy_promise/negatives.ts +++ /dev/null
@@ -1,10 +0,0 @@ -function expect(x: {}): ClassWithTruthy { - return new ClassWithTruthy(); -} - -class ClassWithTruthy { - toBeTruthy() {} -} - -new ClassWithTruthy().toBeTruthy(); -expect(Promise.resolve(1)).toBeTruthy();
diff --git a/packages/concatjs/internal/tsetse/tests/ban_expect_truthy_promise/positives.ts b/packages/concatjs/internal/tsetse/tests/ban_expect_truthy_promise/positives.ts deleted file mode 100644 index 1dfe40f..0000000 --- a/packages/concatjs/internal/tsetse/tests/ban_expect_truthy_promise/positives.ts +++ /dev/null
@@ -1,55 +0,0 @@ -import './jasmine_types'; - -function returnsPromise() { - return Promise.resolve(true); -} - -type Future = Promise<number>; -function aliasedPromise(): Future { - return Promise.resolve(1); -} - -class Extended extends Promise<number> {} -function extendedPromise(): Extended { - return Promise.resolve(1); -} - -class DoubleExtended extends Extended {} -function doubleExtendedPromise(): Extended { - return Promise.resolve(1); -} - -function maybePromise(): Promise<number>|number { - return 3; -} - -describe('example test', () => { - it('should error when expecting promises toBeTruthy', async () => { - const promise = returnsPromise(); - - // Every expect here should fail the check. - expect(promise).toBeTruthy(); - expect(maybePromise()).not.toBeTruthy(); - expect(maybePromise()).toBeTruthy(); - expect(returnsPromise()).toBeTruthy(); - expect(aliasedPromise()).toBeTruthy(); - expect(extendedPromise()).toBeTruthy(); - expect(doubleExtendedPromise()).toBeTruthy(); - }); - - it('should allow awaited promises', async () => { - const promise = returnsPromise(); - expect(await promise).toBeTruthy(); - expect(await maybePromise()).toBeTruthy(); - expect(await returnsPromise()).toBeTruthy(); - expect(await aliasedPromise()).toBeTruthy(); - expect(await extendedPromise()).toBeTruthy(); - }); - - it('should only error for promises', async () => { - expect('truthy').toBeTruthy(); - expect(1).toBeTruthy(); - expect(true).toBeTruthy(); - expect(null).not.toBeTruthy(); - }); -});
diff --git a/packages/concatjs/internal/tsetse/tests/ban_mutable_exports/examples.ts b/packages/concatjs/internal/tsetse/tests/ban_mutable_exports/examples.ts deleted file mode 100644 index c687cad..0000000 --- a/packages/concatjs/internal/tsetse/tests/ban_mutable_exports/examples.ts +++ /dev/null
@@ -1,30 +0,0 @@ -/** - * @fileoverview Examples for the mutable exports rule. - * We expect every 'bad' to be an error, and every 'ok' to pass. - * These are checked as expected diagnostics in the BUILD file. - */ - -export let bad1 = 3; -export var bad2 = 3; -export var bad3 = 3, bad4 = 3; -var bad5 = 3; -export {bad5}; -let bad6 = 3; -export {bad6}; -export {bad6 as bad6alias}; -var bad7 = 3; -export {bad7 as default}; -export let {bad8} = { - bad8: 3 -}; -export let bad9: unknown; - -let ok1 = 3; -var ok2 = 3; -export const ok3 = 3; -const ok4 = 3; -const ok5 = 3; -export {ok5}; -export type ok6 = string; -export function ok7() {} -export class ok8 {}
diff --git a/packages/concatjs/internal/tsetse/tests/ban_promise_as_condition/BUILD b/packages/concatjs/internal/tsetse/tests/ban_promise_as_condition/BUILD deleted file mode 100644 index c6ccb67..0000000 --- a/packages/concatjs/internal/tsetse/tests/ban_promise_as_condition/BUILD +++ /dev/null
@@ -1,47 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -load("//packages/concatjs:index.bzl", "ts_library") - -licenses(["notice"]) # Apache 2.0 - -error_message = "error TS21226: Found a thenable.*" - -ts_library( - name = "positives", - testonly = 1, - srcs = [ - "positives.ts", - ], - expected_diagnostics = [ - "\\(7,7\\).*" + error_message, - "\\(15,7\\).*" + error_message, - "\\(23,19\\).*" + error_message, - "\\(25,10\\).*" + error_message, - "\\(30,34\\).*" + error_message, - "\\(31,34\\).*" + error_message, - "\\(38,19\\).*" + error_message, - "\\(40,10\\).*" + error_message, - "\\(45,34\\).*" + error_message, - "\\(47,34\\).*" + error_message, - ], - tsconfig = "//packages/concatjs/internal:tsetse/tsconfig.json", -) - -ts_library( - name = "negatives", - testonly = 1, - srcs = ["negatives.ts"], - tsconfig = "//packages/concatjs/internal:tsetse/tsconfig.json", -)
diff --git a/packages/concatjs/internal/tsetse/tests/ban_promise_as_condition/negatives.ts b/packages/concatjs/internal/tsetse/tests/ban_promise_as_condition/negatives.ts deleted file mode 100644 index 7b41988..0000000 --- a/packages/concatjs/internal/tsetse/tests/ban_promise_as_condition/negatives.ts +++ /dev/null
@@ -1,96 +0,0 @@ -// tslint:disable -function returnsSynchronous(): boolean { - return false; -} - -async function returnsPromise() { - return false; -} - -async function asyncDoesHitElse() { - if (returnsSynchronous()) { - return true; - } else { - return false; - } -} - -function synchronousIsntAffected() { - if (returnsSynchronous()) { - console.log(true); - } else { - console.log(false); - } - - const ternary = returnsSynchronous() ? 'always' : 'never'; - - while (returnsSynchronous()) { - // stuck in this loop - break; - } - - const a = returnsSynchronous() || 'other thing'; -} - -async function properlyAwaitedGivesNoBuildErrorsDirectCall() { - if (await returnsPromise()) { - console.log(true); - } else { - console.log(false); - } - - const ternary = await returnsPromise() ? 'always' : 'never'; - - while (await returnsPromise()) { - // No longer stuck in this loop - break; - } - - const binaryExpressionBarBar = await returnsPromise() || 'other thing'; - const binaryExpressionAndAnd = await returnsPromise() && 'other thing'; -} - -async function properlyAwaitedGivesNoBuildErrorsPropertyAccess() { - const prom = returnsPromise(); - if (await prom) { - console.log(true); - } else { - console.log(false); - } - - const ternary = await prom ? 'always' : 'never'; - - while (await prom) { - // No longer stuck in this loop - break; - } - - const binaryExpressionBarBar = await prom || 'other thing'; - const binaryExpressionAndAnd = await prom && 'other thing'; -} - -const savedPromise = returnsPromise(); - -// These two verify that it doesn't tag = as a BinaryExpression when it should -// only catch || and && -function takesPromise(prom: Promise<boolean>) {} - -function s() { - let p = returnsPromise(); - - takesPromise(p = returnsPromise()); -} - -// This verifies that Promise|null can still be checked with an if -function takesPromiseMaybe(prom: Promise<boolean>|null|undefined) { - if (prom) { - // do stuff - } -} - -// Checking something exists with && before calling it is legitimate -const boo = { - sendPromise: async () => { - return true; - } -};
diff --git a/packages/concatjs/internal/tsetse/tests/ban_promise_as_condition/positives.ts b/packages/concatjs/internal/tsetse/tests/ban_promise_as_condition/positives.ts deleted file mode 100644 index 08c6771..0000000 --- a/packages/concatjs/internal/tsetse/tests/ban_promise_as_condition/positives.ts +++ /dev/null
@@ -1,48 +0,0 @@ -// tslint:disable -async function returnsPromise() { - return false; -} - -function neverHitsElse() { - if (returnsPromise()) { - return true; - } else { - return false; - } -} - -async function asyncNeverHitsElse() { - if (returnsPromise()) { - return true; - } else { - return false; - } -} - -function detectsFunctionCalls() { - const ternary = returnsPromise() ? 'always' : 'never'; - - while (returnsPromise()) { - // stuck in this loop - break; - } - - const binaryExpressionBarBar = returnsPromise() || 'other thing'; - const binaryExpressionAndAnd = returnsPromise() && 'other thing'; -} - -function detectsPropertyAccesses() { - const prom = returnsPromise(); - - // @ts-ignore TS2801 - const ternary = prom ? 'always' : 'never'; - - while (prom) { - // stuck in this loop - break; - } - - const binaryExpressionBarBar = prom || 'other thing'; - // @ts-ignore TS2801 - const binaryExpressionAndAnd = prom && 'other thing'; -}
diff --git a/packages/concatjs/internal/tsetse/tests/ban_string_initialized_sets/negatives.ts b/packages/concatjs/internal/tsetse/tests/ban_string_initialized_sets/negatives.ts deleted file mode 100644 index e9a188a..0000000 --- a/packages/concatjs/internal/tsetse/tests/ban_string_initialized_sets/negatives.ts +++ /dev/null
@@ -1,42 +0,0 @@ -// tslint:disable -function emptySet() { - const set = new Set(); -} - -function noConstructorArgs() { - const set = new Set; -} - -function nonStringSet() { - const set = new Set([1, 2, 3]); -} - -// This is an allowable way to create a set of strings -function setOfStrings() { - const set = new Set(['abc']); -} - -function setOfChars() { - const set = new Set('abc'.split('')); -} - -function explicitlyAllowString() { - const set = new Set('abc' as Iterable<string>); -} - -// checks that just a property called 'Set' doesn't trigger the error -function justAKeyCalledSet(obj: {Set: {new (s: string): any}}) { - const set = new obj.Set('abc'); -} - -function destructuredConstructorCalledSet(obj: {Set: {new (s: string): any}}) { - const {Set} = obj; - const set = new Set('abc'); -} - -function locallyDeclaredSet() { - class Set { - constructor(private s: string) {} - } - const set = new Set('abc'); -}
diff --git a/packages/concatjs/internal/tsetse/tests/ban_string_initialized_sets/positives.ts b/packages/concatjs/internal/tsetse/tests/ban_string_initialized_sets/positives.ts deleted file mode 100644 index a4ec695..0000000 --- a/packages/concatjs/internal/tsetse/tests/ban_string_initialized_sets/positives.ts +++ /dev/null
@@ -1,51 +0,0 @@ -// tslint:disable -function setWithStringLiteral() { - const set = new Set('abc'); -} - -function setWithStringVariable(s: string) { - const set = new Set(s); -} - -function setWithStringUnionType(s: string|string[]) { - const set = new Set(s); -} - -function setWithStringExpression(fn: () => string) { - const set = new Set(fn()); -} - -function setWithStringExpression2() { - const set = new Set(Math.random() < 0.5 ? 'a' : 'b'); -} - -type TypeA = string|Set<string>; -type TypeB = TypeA|(Iterable<string>&IterableIterator<string>); -function setWithComplexInitializationType(s: TypeB) { - const set = new Set(s); -} - -function setWithUnionStringType(s: string&{toString(): string}) { - const set = new Set(s); -} - -function setWithLocalAlias() { - const TotallyNotASet = Set; - const set = new TotallyNotASet('abc'); -} - -function setWithMultipleAliases() { - const Foo = Set; - const Bar = Foo; - const Baz = Bar; - const set = new Baz('abc'); -} - -function setUsingSetConstructorType(ctor: SetConstructor) { - const set = new ctor('abc'); -} - -type MySet = SetConstructor; -function setUsingAliasedSetConstructor(ctor: MySet) { - const set = new ctor('abc'); -}
diff --git a/packages/concatjs/internal/tsetse/tests/check_return_value/BUILD b/packages/concatjs/internal/tsetse/tests/check_return_value/BUILD deleted file mode 100644 index 4a4c0b0..0000000 --- a/packages/concatjs/internal/tsetse/tests/check_return_value/BUILD +++ /dev/null
@@ -1,66 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -load("//packages/concatjs:index.bzl", "ts_library") - -licenses(["notice"]) # Apache 2.0 - -error_message = "TS21222: return value is unused.\\n\\tSee http://tsetse.info/check-return-value" - -ts_library( - name = "no_expected_diagnostics_test", - testonly = 1, - srcs = ["no_expected_diagnostics.ts"], - tsconfig = "//packages/concatjs/internal:tsetse/tsconfig.json", -) - -ts_library( - name = "expected_diagnostics_test", - testonly = 1, - srcs = ["expected_diagnostics.ts"], - expected_diagnostics = [ - "\\(6,1\\).*" + error_message, - "\\(8,1\\).*" + error_message, - "\\(12,1\\).*" + error_message, - "\\(16,1\\).*" + error_message, - "\\(19,1\\).*" + error_message, - ], - tsconfig = "//packages/concatjs/internal:tsetse/tsconfig.json", -) - -ts_library( - name = "user_defined_check_return_value", - testonly = 1, - srcs = ["user_defined_check_return_value.ts"], - tsconfig = "//packages/concatjs/internal:tsetse/tsconfig.json", -) - -ts_library( - name = "unused_return_value_user_defined_function", - testonly = 1, - srcs = ["unused_return_value_user_defined_function.ts"], - expected_diagnostics = [ - "\\(4,1\\).*" + error_message, - "\\(5,1\\).*" + error_message, - "\\(7,1\\).*" + error_message, - "\\(9,1\\).*" + error_message, - "\\(15,1\\).*" + error_message, - "\\(16,1\\).*" + error_message, - ], - tsconfig = "//packages/concatjs/internal:tsetse/tsconfig.json", - deps = [ - ":user_defined_check_return_value", - "@npm//@types/jasmine", - ], -)
diff --git a/packages/concatjs/internal/tsetse/tests/check_return_value/expected_diagnostics.ts b/packages/concatjs/internal/tsetse/tests/check_return_value/expected_diagnostics.ts deleted file mode 100644 index 691f11a..0000000 --- a/packages/concatjs/internal/tsetse/tests/check_return_value/expected_diagnostics.ts +++ /dev/null
@@ -1,19 +0,0 @@ -export {}; - -// string.trim() result is unused -let stringUnused; -stringUnused = 'hello'; -stringUnused.trim(); -const stringLiteralUnused = 'hello'; -stringLiteralUnused.trim(); - -// Array.concat() result is unused. -const arrayOfStringsUnused = ['hello']; -arrayOfStringsUnused.concat(arrayOfStringsUnused); - -// Object.create() result is unused -const objectUnused = {}; -Object.create(objectUnused); - -// string.replace() with a substring -stringUnused.replace('o', 'O');
diff --git a/packages/concatjs/internal/tsetse/tests/check_return_value/no_expected_diagnostics.ts b/packages/concatjs/internal/tsetse/tests/check_return_value/no_expected_diagnostics.ts deleted file mode 100644 index c1b3abd..0000000 --- a/packages/concatjs/internal/tsetse/tests/check_return_value/no_expected_diagnostics.ts +++ /dev/null
@@ -1,32 +0,0 @@ -// Array.pop() has side effects -const arrayUsedWithSideEffects = [1]; -arrayUsedWithSideEffects.pop(); - -// string.replace() result is assigned to a variable -const stringFunctionAssignedToVar = 'hello'; -let world; -world = stringFunctionAssignedToVar.replace('hello', 'world'); - -// string.replace() result is used as an argument to another function -const stringUsedInFunctionArguments = 'hello'; -console.debug(stringUsedInFunctionArguments.replace('hello', 'world')); - -// string.replace() result is returned -export function foo(): string { - const stringUsedInFunctionReturn = 'hello'; - return stringUsedInFunctionReturn.replace('hello', 'world'); -} - -function aRandomFunctionShouldNotTriggerThisRule() {} -aRandomFunctionShouldNotTriggerThisRule(); - -const stringFunctionUsedInIf = 'hello'; -if (stringFunctionUsedInIf.trim()) { -} - -// string.replace() with a function -const matches: string[] = []; -'hello'.replace('l', (s) => { - matches.push(s); - return s; -});
diff --git a/packages/concatjs/internal/tsetse/tests/check_return_value/unused_return_value_user_defined_function.ts b/packages/concatjs/internal/tsetse/tests/check_return_value/unused_return_value_user_defined_function.ts deleted file mode 100644 index 05c1142..0000000 --- a/packages/concatjs/internal/tsetse/tests/check_return_value/unused_return_value_user_defined_function.ts +++ /dev/null
@@ -1,16 +0,0 @@ -import 'jasmine'; -import {ClassContainingUserDefinedCheckReturnValueFunction, manyJsDocTags, userDefinedCheckReturnValueFunction} from './user_defined_check_return_value'; - -userDefinedCheckReturnValueFunction('hello'); -manyJsDocTags('hello'); - -new ClassContainingUserDefinedCheckReturnValueFunction().checkReturnValue( - 'hello'); -new ClassContainingUserDefinedCheckReturnValueFunction().sameLineJsDoc('hello'); -new ClassContainingUserDefinedCheckReturnValueFunction().noJsDoc('hello'); - -const nested = { - child: new ClassContainingUserDefinedCheckReturnValueFunction() -}; -nested.child.checkReturnValue('hello'); -expect(null);
diff --git a/packages/concatjs/internal/tsetse/tests/check_return_value/user_defined_check_return_value.ts b/packages/concatjs/internal/tsetse/tests/check_return_value/user_defined_check_return_value.ts deleted file mode 100644 index 0965983..0000000 --- a/packages/concatjs/internal/tsetse/tests/check_return_value/user_defined_check_return_value.ts +++ /dev/null
@@ -1,30 +0,0 @@ -/** - * @checkReturnValue The input string is unchanged. - */ -export function userDefinedCheckReturnValueFunction(str: string) { - return `input ${str}`; -} - -/** - * @tag1 - * @checkReturnValue The input string is unchanged. - * @tag2 - */ -export function manyJsDocTags(str: string) { - return `input ${str}`; -} - -export class ClassContainingUserDefinedCheckReturnValueFunction { - /** - * @checkReturnValue The input string is unchanged. - */ - checkReturnValue(str: string) { - return `input ${str}`; - } - /** @checkReturnValue */ sameLineJsDoc(str: string) { - return `input ${str}`; - } - noJsDoc(str: string) { - return `input ${str}`; - } -}
diff --git a/packages/concatjs/internal/tsetse/tests/equals_nan/BUILD b/packages/concatjs/internal/tsetse/tests/equals_nan/BUILD deleted file mode 100644 index 5e0918f..0000000 --- a/packages/concatjs/internal/tsetse/tests/equals_nan/BUILD +++ /dev/null
@@ -1,42 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -load("//packages/concatjs:index.bzl", "ts_library") - -licenses(["notice"]) # Apache 2.0 - -ts_library( - name = "positives", - testonly = 1, - srcs = [ - "positives.ts", - ], - expected_diagnostics = [ - "\\(2,19\\): error TS21223: x === NaN is always false; use isNaN\\(x\\) instead", - "\\(6,5\\): error TS21223: x === NaN is always false; use isNaN\\(x\\) instead", - "\\(7,5\\): error TS21223: x == NaN is always false; use isNaN\\(x\\) instead", - "\\(8,5\\): error TS21223: x !== NaN is always true; use !isNaN\\(x\\) instead", - "\\(9,5\\): error TS21223: x != NaN is always true; use !isNaN\\(x\\) instead", - "\\(11,1\\): error TS21223: x === NaN is always false; use isNaN\\(x\\) instead", - "\\(12,1\\): error TS21223: x === NaN is always false; use isNaN\\(x\\) instead", - ], -) - -ts_library( - name = "negatives", - testonly = 1, - srcs = [ - "negatives.ts", - ], -)
diff --git a/packages/concatjs/internal/tsetse/tests/equals_nan/negatives.ts b/packages/concatjs/internal/tsetse/tests/equals_nan/negatives.ts deleted file mode 100644 index dcb13d3..0000000 --- a/packages/concatjs/internal/tsetse/tests/equals_nan/negatives.ts +++ /dev/null
@@ -1,6 +0,0 @@ -// tslint:disable -isNaN(1); -let x = NaN; -3 === 0 / 0; - -export {} // Make this file a module.
diff --git a/packages/concatjs/internal/tsetse/tests/equals_nan/positives.ts b/packages/concatjs/internal/tsetse/tests/equals_nan/positives.ts deleted file mode 100644 index 9556f23..0000000 --- a/packages/concatjs/internal/tsetse/tests/equals_nan/positives.ts +++ /dev/null
@@ -1,14 +0,0 @@ -// tslint:disable -const equalsNan = 1 === NaN; - -declare const x: number; - -if (x === NaN) alert('never happens'); -if (x == NaN) alert('never happens'); -if (x !== NaN) alert('always happens'); -if (x != NaN) alert('always happens'); - -NaN === NaN; -NaN === 0 / 0; - -export {} // Make this file a module.
diff --git a/packages/concatjs/internal/tsetse/tests/must_type_assert_json_parse/negatives.ts b/packages/concatjs/internal/tsetse/tests/must_type_assert_json_parse/negatives.ts deleted file mode 100644 index 6c8e7c3..0000000 --- a/packages/concatjs/internal/tsetse/tests/must_type_assert_json_parse/negatives.ts +++ /dev/null
@@ -1,27 +0,0 @@ -// tslint:disable -if (JSON.parse('false') as boolean) { - alert('never happens'); -} - -if (JSON.parse('null') as null) { - alert('never happens'); -} - -declare interface MyInterface { - attr: string; -} - -const a = JSON.parse('{}') as any; -const b = JSON.parse('{}') as unknown; -const c = JSON.parse('{}') as MyInterface; - -let d = JSON.parse('{}') as any; -let e = JSON.parse('{}') as unknown; -let f = JSON.parse('{}') as MyInterface; - -{ - const JSON = {parse: (a: any) => a}; - let g = JSON.parse('{}'); // User defined JSON, so no error. -} - -export {}; // Make this file a module.
diff --git a/packages/concatjs/internal/tsetse/tests/must_type_assert_json_parse/positives.ts b/packages/concatjs/internal/tsetse/tests/must_type_assert_json_parse/positives.ts deleted file mode 100644 index ce013ef..0000000 --- a/packages/concatjs/internal/tsetse/tests/must_type_assert_json_parse/positives.ts +++ /dev/null
@@ -1,9 +0,0 @@ -// tslint:disable - -const a = JSON.parse('{}'); -let b = JSON.parse('{}'); -if (JSON.parse('false')) { - alert('never happens'); -} - -export {}; // Make this file a module.
diff --git a/packages/concatjs/internal/tsetse/tests/must_use_promises/BUILD b/packages/concatjs/internal/tsetse/tests/must_use_promises/BUILD deleted file mode 100644 index 6050795..0000000 --- a/packages/concatjs/internal/tsetse/tests/must_use_promises/BUILD +++ /dev/null
@@ -1,35 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -load("//packages/concatjs:index.bzl", "ts_library") - -licenses(["notice"]) # Apache 2.0 - -error_message = "TS21225: All Promises in async functions must either be awaited or used in an expression." - -ts_library( - name = "positives", - testonly = 1, - srcs = [ - "positives.ts", - ], - expected_diagnostics = [ - "\\(29,3\\)" + error_message, - "\\(30,3\\)" + error_message, - "\\(31,3\\)" + error_message, - "\\(32,3\\)" + error_message, - "\\(34,3\\)" + error_message, - ], - tsconfig = "//packages/concatjs/internal:tsetse/tsconfig.json", -)
diff --git a/packages/concatjs/internal/tsetse/tests/must_use_promises/no_await.ts b/packages/concatjs/internal/tsetse/tests/must_use_promises/no_await.ts deleted file mode 100644 index d2b9adc..0000000 --- a/packages/concatjs/internal/tsetse/tests/must_use_promises/no_await.ts +++ /dev/null
@@ -1,9 +0,0 @@ -import {noAwait} from 'google3/javascript/typescript/contrib/async'; - -function returnsPromise() { - return Promise.resolve(1); -} - -async function unusedPromises() { - noAwait(returnsPromise()); -}
diff --git a/packages/concatjs/internal/tsetse/tests/must_use_promises/positives.ts b/packages/concatjs/internal/tsetse/tests/must_use_promises/positives.ts deleted file mode 100644 index 659802a..0000000 --- a/packages/concatjs/internal/tsetse/tests/must_use_promises/positives.ts +++ /dev/null
@@ -1,54 +0,0 @@ -function isPromise() { - return Promise.resolve(1); -} - -function maybePromise(): Promise<number>|number { - return Promise.resolve(1); -} - -type Future = Promise<number>; -function aliasedPromise(): Future { - return Promise.resolve(1); -} - -class Extended extends Promise<number> {} - -function extendedPromise(): Extended { - return Promise.resolve(1); -} - -function usePromise(maybePromise: Promise<number>|number) { - return false; -} - -async function returnLaterPromise() { - return () => Promise.resolve(1); -} - -async function unusedPromises() { - isPromise(); - maybePromise(); - aliasedPromise(); - extendedPromise(); - const later = await returnLaterPromise(); - later(); -} - -async function hasAwait() { - await isPromise(); - await maybePromise(); - await aliasedPromise(); - await extendedPromise(); - const later = await returnLaterPromise(); - await later(); - usePromise(maybePromise()); -} - -function nonAsyncFunction() { - isPromise(); - maybePromise(); - aliasedPromise(); - extendedPromise(); - const future = maybePromise(); - usePromise(future); -}
diff --git a/packages/concatjs/internal/tsetse/tests/property_renaming_safe/errors.ts b/packages/concatjs/internal/tsetse/tests/property_renaming_safe/errors.ts deleted file mode 100644 index b4ed844..0000000 --- a/packages/concatjs/internal/tsetse/tests/property_renaming_safe/errors.ts +++ /dev/null
@@ -1,64 +0,0 @@ -/* tslint:disable */ -interface IdxSig { - [key: string]: string; -} - -function propAccess(x: IdxSig) { - x.prop; // error - x['prop']; // ok -} - -function descructuring(x: IdxSig) { - const {prop} = x; // ok, but should be an error. -} - -interface MixedIdxSig extends IdxSig { - namedProp: string; -} - -function mixedPropAccess(x: MixedIdxSig) { - x.namedProp; // ok - x['namedProp']; // ok - x.prop; // error - x['prop']; // ok -} - -function genericAccess<T extends IdxSig>(x: T) { - x.prop; // error - x['prop']; // ok -} - -interface MixedIndexSigUsedInUnion { - [key: string]: string; - namedProp2: string; -} - -function unionType(x: MixedIdxSig|MixedIndexSigUsedInUnion) { - x.prop; // error - x['prop']; // ok -} - -/** - * Curiously Record<string, T> is treated like an index signature. - */ -function recordStringType(x: Record<string, number>) { - x.prop; // error - x['prop']; // ok -} - -/** - * But narrowing the generic parameter to a string literal union exempts it. - */ -function recordNarrowType(x: Record<'prop'|'other', number>) { - x.prop; // ok - x['prop']; // ok -} - -/** - * Similary, to Records mapped types of of 'in string' are threated like a - * string index signature. - */ -function mappedType(x: {[x in string]: boolean}) { - x.prop; // error - x['prop']; // ok -}
diff --git a/packages/concatjs/internal/tsetse/tsconfig.json b/packages/concatjs/internal/tsetse/tsconfig.json deleted file mode 100644 index abc0d83..0000000 --- a/packages/concatjs/internal/tsetse/tsconfig.json +++ /dev/null
@@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "strict": true, - "types": [ - ], - "lib": [ - "dom", - "es5", - "es2015.core", - "es2015.collection", - "es2015.promise" - ], - "plugins": [ - // Run `bazel build internal:tsc_wrapped` from root folder - // (rules_typescript) to build the plugin. - // - // If you are using VSCode make sure you are using the workspace version - // of TS, otherwise the plugin won't be loaded. Also, make sure you have - // opened the repo as workspace (from the root folder). - { - "name": "../../bazel-bin/internal/tsetse/language_service_plugin.js" - } - ] - } -}
diff --git a/packages/concatjs/internal/tsetse/util/absolute_matcher.ts b/packages/concatjs/internal/tsetse/util/absolute_matcher.ts deleted file mode 100644 index 1ba885d..0000000 --- a/packages/concatjs/internal/tsetse/util/absolute_matcher.ts +++ /dev/null
@@ -1,180 +0,0 @@ -import * as ts from 'typescript'; -import {dealias, debugLog, isInStockLibraries, isNameInDeclaration, isPartOfImportStatement} from './ast_tools'; - -const PATH_NAME_FORMAT = '[/\\.\\w\\d_-]+'; -const JS_IDENTIFIER_FORMAT = '[\\w\\d_-]+'; -const FQN_FORMAT = `(${JS_IDENTIFIER_FORMAT}\.)*${JS_IDENTIFIER_FORMAT}`; -const GLOBAL = 'GLOBAL'; -const ANY_SYMBOL = 'ANY_SYMBOL'; -const CLOSURE = 'CLOSURE'; -/** A fqn made out of a dot-separated chain of JS identifiers. */ -const ABSOLUTE_RE = new RegExp(`^${PATH_NAME_FORMAT}\\|${FQN_FORMAT}$`); -/** - * Clutz glues js symbols to ts namespace by prepending "ಠ_ಠ.clutz.". - * We need to include this prefix when the banned name is from Closure. - */ -const CLUTZ_SYM_PREFIX = 'ಠ_ಠ.clutz.'; - -/** - * This class matches symbols given a "foo.bar.baz" name, where none of the - * steps are instances of classes. - * - * Note that this isn't smart about subclasses and types: to write a check, we - * strongly suggest finding the expected symbol in externs to find the object - * name on which the symbol was initially defined. - * - * This matcher requires a scope for the symbol, which may be `GLOBAL`, - * `ANY_SYMBOL`, `CLOSURE` or a file path filter. `CLOSURE` indicates that the - * symbol is from the JS Closure library processed by clutz. The matcher begins - * with this scope, then the separator "|", followed by the symbol name. For - * example, "GLOBAL|eval". - * - * The file filter specifies - * (part of) the path of the file in which the symbol of interest is defined. - * For example, "path/to/file.ts|foo.bar.baz". - * With this filter, only symbols named "foo.bar.baz" that are defined in a path - * that contains "path/to/file.ts" are matched. - * - * This filter is useful when mutiple symbols have the same name but - * you want to match with a specific one. For example, assume that there are - * two classes named "Foo" defined in /path/to/file0 and /path/to/file1. - * // in /path/to/file0 - * export class Foo { static bar() {return "Foo.bar in file0";} } - * - * // in /path/to/file1 - * export class Foo { static bar() {return "Foo.bar in file1";} } - * - * Suppose that these two classes are referenced in two other files. - * // in /path/to/file2 - * import {Foo} from /path/to/file0; - * Foo.bar(); - * - * // in /path/to/file3 - * import {Foo} from /path/to/file1; - * Foo.bar(); - * - * An absolute matcher "Foo.bar" without a file filter will match with both - * references to "Foo.bar" in /path/to/file2 and /path/to/file3. - * An absolute matcher "/path/to/file1|Foo.bar", however, only matches with the - * "Foo.bar()" in /path/to/file3 because that references the "Foo.bar" defined - * in /path/to/file1. - * - * Note that an absolute matcher will match with any reference to the symbol - * defined in the file(s) specified by the file filter. For example, assume that - * Foo from file1 is extended in file4. - * - * // in /path/to/file4 - * import {Foo} from /path/to/file1; - * class Moo { static tar() {return "Moo.tar in file4";} } - * Moo.bar(); - * - * An absolute matcher "/path/to/file1|Foo.bar" matches with "Moo.bar()" because - * "bar" is defined as part of Foo in /path/to/file1. - */ -export class AbsoluteMatcher { - /** - * From a "path/to/file.ts|foo.bar.baz", builds a Matcher. - */ - readonly filePath: string; - readonly bannedName: string; - - constructor(spec: string) { - if (!spec.match(ABSOLUTE_RE)) { - throw new Error('Malformed matcher selector.'); - } - - // JSConformance used to use a Foo.prototype.bar syntax for bar on - // instances of Foo. TS doesn't surface the prototype part in the FQN, and - // so you can't tell static `bar` on `foo` from the `bar` property/method - // on `foo`. To avoid any confusion, throw there if we see `prototype` in - // the spec: that way, it's obvious that you're not trying to match - // properties. - if (spec.match('.prototype.')) { - throw new Error( - 'Your pattern includes a .prototype, but the AbsoluteMatcher is ' + - 'meant for non-object matches. Use the PropertyMatcher instead, or ' + - 'the Property-based PatternKinds.'); - } - - // Split spec by the separator "|". - [this.filePath, this.bannedName] = spec.split('|', 2); - - if (this.filePath === CLOSURE) { - this.bannedName = CLUTZ_SYM_PREFIX + this.bannedName; - } - } - - matches(n: ts.Node, tc: ts.TypeChecker): boolean { - debugLog(() => `start matching ${n.getText()} in ${n.parent.getText()}`); - - // Check if the node is being declared. Declaration may be imported without - // programmer being aware of. We should not alert them about that. - // Since import statments are also declarations, this have two notable - // consequences. - // - Match is negative for imports without renaming - // - Match is positive for imports with renaming, when the imported name - // is the target. Since Tsetse is flow insensitive and we don't track - // symbol aliases, the import statement is the only place we can match - // bad symbols if they get renamed. - if (isNameInDeclaration(n)) { - debugLog(() => `We don't flag symbol declarations`); - return false; - } - - // Get the symbol (or the one at the other end of this alias) that we're - // looking at. - const s = dealias(tc.getSymbolAtLocation(n), tc); - if (!s) { - debugLog(() => `cannot get symbol`); - return false; - } - - // The TS-provided FQN tells us the full identifier, and the origin file - // in some circumstances. - const fqn = tc.getFullyQualifiedName(s); - debugLog(() => `got FQN ${fqn}`); - - // Name-based check: `getFullyQualifiedName` returns `"filename".foo.bar` or - // just `foo.bar` if the symbol is ambient. The check here should consider - // both cases. - if (!fqn.endsWith('".' + this.bannedName) && fqn !== this.bannedName) { - debugLog(() => `FQN ${fqn} doesn't match name ${this.bannedName}`); - return false; - } - - // If `ANY_SYMBOL` or `CLOSURE` is specified, it's sufficient to conclude we - // have a match. - if (this.filePath === ANY_SYMBOL || this.filePath === CLOSURE) { - return true; - } - - // If there is no declaration, the symbol is a language built-in object. - // This is a match only if `GLOBAL` is specified. - const declarations = s.getDeclarations(); - if (declarations === undefined) { - return this.filePath === GLOBAL; - } - - // No file info in the FQN means it's imported from a .d.ts declaration - // file. This can be from a core library, a JS library, or an exported local - // symbol defined in another TS target. We need to extract the name of the - // declaration file. - if (!fqn.startsWith('"')) { - if (this.filePath === GLOBAL) { - return declarations.some(isInStockLibraries); - } else { - return declarations.some((d) => { - const srcFilePath = d.getSourceFile()?.fileName; - return srcFilePath && srcFilePath.match(this.filePath); - }) - } - } else { - const last = fqn.indexOf('"', 1); - if (last === -1) { - throw new Error('Malformed fully-qualified name.'); - } - const filePath = fqn.substring(1, last); - return filePath.match(this.filePath) !== null; - } - } -}
diff --git a/packages/concatjs/internal/tsetse/util/absolute_matcher_test.ts b/packages/concatjs/internal/tsetse/util/absolute_matcher_test.ts deleted file mode 100644 index 9f612c6..0000000 --- a/packages/concatjs/internal/tsetse/util/absolute_matcher_test.ts +++ /dev/null
@@ -1,345 +0,0 @@ -import 'jasmine'; -import {ConformancePatternRule, ErrorCode, PatternKind} from '../rules/conformance_pattern_rule'; -import {compileAndCheck, customMatchers} from './testing/test_support'; - -describe('AbsoluteMatcher', () => { - beforeEach(() => { - jasmine.addMatchers(customMatchers); - }); - - it('requires a matcher scope', () => { - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'banned name with no scope', - kind: PatternKind.BANNED_NAME, - values: ['exec'] - }; - const sources = [`eval('alert("hi");');`]; - - const check = () => - compileAndCheck(new ConformancePatternRule(config), ...sources); - - expect(check).toThrowError('Malformed matcher selector.'); - }); - - describe('file scope', () => { - it('matches a file path', () => { - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'banned name with file path', - kind: PatternKind.BANNED_NAME, - values: ['./file_0|Foo.bar'] - }; - const sources = [ - `export class Foo { static bar(s: string) {return s + "abc";} }`, - `import {Foo} from './file_0'; - var a = Foo.bar("123");` - ]; - const results = - compileAndCheck(new ConformancePatternRule(config), ...sources); - - expect(results).toHaveFailuresMatching( - {matchedCode: `bar`, messageText: 'banned name with file path'}); - }); - - it('ignores an exported symbol defined in an unmatched file path', () => { - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'banned name with file path', - kind: PatternKind.BANNED_NAME, - values: ['./file_0|Foo.bar'] - }; - - // test exported symbols - const sources = [ - `export class Foo { static bar(s: string) {return s + "abc";} }`, - `export class Foo { static bar(s: string) {return s + "abc";} }`, - `import {Foo} from './file_1'; - var a = Foo.bar("123");` - ]; - - const results = - compileAndCheck(new ConformancePatternRule(config), ...sources); - expect(results).toHaveNoFailures(); - }); - - it('ignores an un-exported symbol defined in an unmatched file path', - () => { - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'banned name with file path', - kind: PatternKind.BANNED_NAME, - values: ['./file_0|Foo.bar'] - }; - - // test non-exported symbols - const sources = [ - `export class Foo { static bar(s: string) {return s + "abc";} }`, - `class Foo { static bar(s: string) {return s + "abc";} } - var a = Foo.bar("123");` - ]; - - const results = - compileAndCheck(new ConformancePatternRule(config), ...sources); - - expect(results).toHaveNoFailures(); - }); - - it('matches a local exported definition', () => { - // This is a match because Foo.bar is an exported symbol. - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'banned name with file path', - kind: PatternKind.BANNED_NAME, - values: ['./file_0|Foo.bar'] - }; - const sources = - [`export class Foo { static bar(s: string) {return s + "abc";} } - var a = Foo.bar("123");`]; - - const results = - compileAndCheck(new ConformancePatternRule(config), ...sources); - expect(results).toHaveFailuresMatching( - {matchedCode: `bar`, messageText: 'banned name with file path'}); - }); - - it('matches names in import statement', () => { - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'banned name with file path', - kind: PatternKind.BANNED_NAME, - values: ['./file_0|foo', 'ANY_SYMBOL|bar'] - }; - const sources = [ - `export function foo(s: string) {return s + "abc";} - function bar() {} - export {bar};`, - `import {foo} from './file_0'; - import {bar} from './file_0';`, - `import {foo as okFoo} from './file_0'; - import {bar as okBar} from './file_0';`, - ]; - - const results = - compileAndCheck(new ConformancePatternRule(config), ...sources); - expect(results).toHaveFailuresMatching( - {matchedCode: `foo`, messageText: 'banned name with file path'}, - {matchedCode: `bar`, messageText: 'banned name with file path'}, - ); - }); - }); - - describe('global scope', () => { - it('matches an in-stock library method', () => { - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'banned ambient name', - kind: PatternKind.BANNED_NAME, - values: ['GLOBAL|eval'] - }; - const sources = [`eval('alert("hi");');`]; - - const results = - compileAndCheck(new ConformancePatternRule(config), ...sources); - expect(results).toHaveFailuresMatching( - {matchedCode: `eval`, messageText: 'banned ambient name'}); - }); - - it('does not match a custom exported method with the same name', () => { - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'banned ambient name', - kind: PatternKind.BANNED_NAME, - values: ['GLOBAL|eval'] - }; - const sources = - [`export class Foo { static eval(s: string) { return s + "abc";} } - var a = Foo.eval("123");`]; - - const results = - compileAndCheck(new ConformancePatternRule(config), ...sources); - expect(results).toHaveNoFailures(); - }); - - it('does not match a custom non-exported method with the same name', () => { - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'banned global name', - kind: PatternKind.BANNED_NAME, - values: ['GLOBAL|Foo.bar'] - }; - const sources = [`class Foo { static bar(s: string) {return s + "abc";} } - var a = Foo.bar("123");`]; - const results = - compileAndCheck(new ConformancePatternRule(config), ...sources); - expect(results).toHaveNoFailures(); - }); - - it('matches an initializer in a named declaration', () => { - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'banned ambient name', - kind: PatternKind.BANNED_NAME, - values: ['GLOBAL|open'], - }; - const sources = ['const op = open;']; - - const results = - compileAndCheck(new ConformancePatternRule(config), ...sources); - expect(results).toHaveFailuresMatching( - {matchedCode: 'open', messageText: 'banned ambient name'}); - }); - }); - - describe('properties', () => { - it('matches a static property', () => { - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'banned name with file path', - kind: PatternKind.BANNED_NAME, - values: ['./file_0|Foo.s'] - }; - const sources = [ - `export class Foo { static s : string; }`, - `import {Foo} from './file_0'; - var a = Foo.s;`, - ]; - - const results = - compileAndCheck(new ConformancePatternRule(config), ...sources); - expect(results).toHaveFailuresMatching( - {matchedCode: `s`, messageText: 'banned name with file path'}); - }); - - it('does not match a property with a name overlapping an in-stock library', - () => { - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'banned name without file path', - kind: PatternKind.BANNED_NAME, - values: ['GLOBAL|open'] - }; - const sources = [ - 'const elem = new XMLHttpRequest();', - 'elem.open("get", "url");', // FQN of elem.open is - // XMLHttpRequest.open and shouldn't be - // banned - ]; - - const results = - compileAndCheck(new ConformancePatternRule(config), ...sources); - expect(results).toHaveNoFailures(); - }); - }); - - describe('inheritance', () => { - it('matches an inherited static property', () => { - // This is a match because Moo inherits s from Foo. - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'banned name with file path', - kind: PatternKind.BANNED_NAME, - values: ['./file_0|Foo.s'] - }; - const sources = [ - `export class Foo { static s : string; }`, - `import {Foo} from './file_0'; - export class Moo extends Foo { static t : string; }`, - `import {Moo} from './file_1'; - var a = Moo.s;`, - ]; - - const results = - compileAndCheck(new ConformancePatternRule(config), ...sources); - expect(results).toHaveFailuresMatching( - {matchedCode: `s`, messageText: 'banned name with file path'}); - }); - - it('matches an inherited static method', () => { - // This is a match because Moo inherits bar from Foo. - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'banned name with file path', - kind: PatternKind.BANNED_NAME, - values: ['./file_0|Foo.bar'] - }; - const sources = [ - `export class Foo { static bar(s: string) {return s + "abc";} }`, - `import {Foo} from './file_0'; - export class Moo extends Foo { static far(s: string) {return s + "def";} }`, - `import {Moo} from './file_1'; - Moo.bar("abc");` - ]; - - const results = - compileAndCheck(new ConformancePatternRule(config), ...sources); - expect(results).toHaveFailuresMatching( - {matchedCode: `bar`, messageText: 'banned name with file path'}); - }); - - it('does not match a redefined inherited static property', () => { - // This is not a match because Moo redefines s. - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'banned name with file path', - kind: PatternKind.BANNED_NAME, - values: ['./file_0|Foo.s'] - }; - const sources = [ - `export class Foo { static s : string; }`, - `import {Foo} from './file_0'; - export class Moo extends Foo { static s : string; }`, - `import {Moo} from './file_1'; - var a = Moo.s;`, - ]; - - const results = - compileAndCheck(new ConformancePatternRule(config), ...sources); - expect(results).toHaveNoFailures(); - }); - - it('does not match a redefined inherited static method', () => { - // This is not a match because Moo redefines bar. - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'banned name with file path', - kind: PatternKind.BANNED_NAME, - values: ['./file_0|Foo.bar'] - }; - const sources = [ - `export class Foo { static bar(s: string) {return s + "abc";} } - export class Moo extends Foo { static bar(s: string) {return s + "def";} }`, - `import {Foo, Moo} from './file_0'; - Moo.bar("abc");` - ]; - - const results = - compileAndCheck(new ConformancePatternRule(config), ...sources); - expect(results).toHaveNoFailures(); - }); - - it('does not match an interface\'s static method', () => { - // This is not a match because even though bar specified is interface Moo, - // its actual definition is in class Boo. - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'banned name with file path', - kind: PatternKind.BANNED_NAME, - values: ['./file_1|Moo.bar'] - }; - const sources = [ - `export class Foo { static bar(s: string) {return s + "abc";} }`, - `import {Foo} from './file_0'; - export interface Moo extends Foo { }`, - `import {Moo} from './file_1'; - export class Boo implements Moo { static bar(s: string) {return s + "def";} }`, - `import {Boo} from './file_2'; - Boo.bar("abc");`, - ]; - - const results = - compileAndCheck(new ConformancePatternRule(config), ...sources); - expect(results).toHaveNoFailures(); - }); - }); -});
diff --git a/packages/concatjs/internal/tsetse/util/allowlist.ts b/packages/concatjs/internal/tsetse/util/allowlist.ts deleted file mode 100644 index 07f64e9..0000000 --- a/packages/concatjs/internal/tsetse/util/allowlist.ts +++ /dev/null
@@ -1,96 +0,0 @@ -/** - * An exemption list entry, corresponding to a logical exemption rule. Use these - * to distinguish between various logical reasons for exempting something: - * for instance, tie these to particular bugs that needed to be exempted, per - * legacy project, manually reviewed entries, and so on. - * - * Exemption lists are based on the file paths provided by the TS compiler, with - * both regexp-based checks and prefix-based checks. - * - * - * Follows the logic in - * https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/conformance.proto. - */ -export interface AllowlistEntry { - /** The category corresponding to this entry. */ - readonly reason: ExemptionReason; - /** Why is this okay to be exempted?. */ - readonly explanation?: string; - - /** - * Regexps for the paths of files that will be ignored by the - * ConformancePattern. Beware, escaping can be tricky. - */ - readonly regexp?: readonly string[]; - /** - * Prefixes for the paths of files that will be ignored by the - * ConformancePattern. - */ - readonly prefix?: readonly string[]; -} - -/** - * The categories of exemption entries. - */ -export enum ExemptionReason { - /** No reason. */ - UNSPECIFIED, - /** Code that has to be grandfathered in (no guarantees). */ - LEGACY, - /** - * Code that does not enter the scope of this particular check (no - * guarantees). - */ - OUT_OF_SCOPE, - /** Manually reviewed exceptions (supposedly okay). */ - MANUALLY_REVIEWED -} - -/** - * A complete allowlist with all related AllowlistEntry grouped together, with - * ExemptionReason ignored since it is purely for documentary purposes. - */ -export class Allowlist { - private readonly allowlistedPrefixes: readonly string[] = []; - private readonly allowlistedRegExps: readonly RegExp[] = []; - // To avoid repeated computation for allowlisting queries with the same file - // path, create a memoizer to cache known results. This is useful in watch - // mode (and possible in language service) when the same files can be compiled - // repeatedly. - private readonly allowlistMemoizer = new Map<string, boolean>(); - - constructor(allowlistEntries?: AllowlistEntry[]) { - if (allowlistEntries) { - for (const e of allowlistEntries) { - if (e.prefix) { - this.allowlistedPrefixes = - this.allowlistedPrefixes.concat(...e.prefix); - } - if (e.regexp) { - this.allowlistedRegExps = this.allowlistedRegExps.concat( - ...e.regexp.map(r => new RegExp(r))); - } - } - } - } - - isAllowlisted(filePath: string): boolean { - if (this.allowlistMemoizer.has(filePath)) { - return this.allowlistMemoizer.get(filePath)!; - } - for (const p of this.allowlistedPrefixes) { - if (filePath.startsWith(p)) { - this.allowlistMemoizer.set(filePath, true); - return true; - } - } - for (const re of this.allowlistedRegExps) { - if (re.test(filePath)) { - this.allowlistMemoizer.set(filePath, true); - return true; - } - } - this.allowlistMemoizer.set(filePath, false); - return false; - } -}
diff --git a/packages/concatjs/internal/tsetse/util/allowlist_test.ts b/packages/concatjs/internal/tsetse/util/allowlist_test.ts deleted file mode 100644 index d451c76..0000000 --- a/packages/concatjs/internal/tsetse/util/allowlist_test.ts +++ /dev/null
@@ -1,163 +0,0 @@ -import 'jasmine'; - -import {ConformancePatternRule, ErrorCode, PatternKind} from '../rules/conformance_pattern_rule'; - -import {ExemptionReason} from './allowlist'; -import {compileAndCheck, customMatchers, getTempDirForAllowlist} from './testing/test_support'; - -const tmpPrefixForAllowlist = getTempDirForAllowlist(); -const tmpRegexpForAllowlist = - `^(?:${getTempDirForAllowlist().replace(/\\/g, '\\\\')})`; - -describe('ConformancePatternRule allowlist handling', () => { - const source = `export {};\n` + - `const q = document.createElement('q');\n` + - `q.cite = 'some example string';\n`; - - // The initial config off which we run those checks. - const baseConfig = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'do not cite', - kind: PatternKind.BANNED_PROPERTY_WRITE, - values: ['HTMLQuoteElement.prototype.cite'], - }; - - it('matches if no allowlist (sanity check)', () => { - const config = {...baseConfig, allowlistEntries: []}; - const rule = new ConformancePatternRule(config); - const results = compileAndCheck(rule, source); - - expect(results).toHaveNFailures(1); - }); - - it('matches if there is an empty allowlist group', () => { - const config = { - ...baseConfig, - allowlistEntries: [{ - reason: ExemptionReason.UNSPECIFIED, - }] - }; - const rule = new ConformancePatternRule(config); - const results = compileAndCheck(rule, source); - - expect(results).toHaveNFailures(1); - }); - - it('respects prefix-based allowlists (matching test)', () => { - const config = { - ...baseConfig, - allowlistEntries: [{ - reason: ExemptionReason.UNSPECIFIED, - prefix: [tmpPrefixForAllowlist], - }] - }; - const rule = new ConformancePatternRule(config); - const results = compileAndCheck(rule, source); - - expect(results).toHaveNoFailures(); - }); - - it('respects prefix-based allowlists (non-matching test)', () => { - const config = { - ...baseConfig, - allowlistEntries: [{ - reason: ExemptionReason.UNSPECIFIED, - prefix: ['/nowhere in particular/'], - }] - }; - const rule = new ConformancePatternRule(config); - const results = compileAndCheck(rule, source); - - expect(results).toHaveNFailures(1); - }); - - it('respects regex-based allowlists', () => { - const config = { - ...baseConfig, - allowlistEntries: [{ - reason: ExemptionReason.UNSPECIFIED, - regexp: [`${tmpRegexpForAllowlist}.+[\\\\/]file_0\\.ts`] - }] - }; - const rule = new ConformancePatternRule(config); - const results = compileAndCheck(rule, source); - - expect(results).toHaveNoFailures(); - }); - - it('accepts several regex-based allowlists', () => { - const config = { - ...baseConfig, - allowlistEntries: [{ - reason: ExemptionReason.UNSPECIFIED, - regexp: [ - `${tmpRegexpForAllowlist}.+[\\\\/]file_0\\.ts`, - `${tmpRegexpForAllowlist}.+[\\\\/]file_1\\.ts` - ] - }] - }; - const rule = new ConformancePatternRule(config); - // Testing two times the same file so that both regexps match. - const results = compileAndCheck(rule, source, source); - - expect(results).toHaveNoFailures(); - }); - - it('throws on creation of invalid regexps', () => { - const config = { - ...baseConfig, - allowlistEntries: [{ - reason: ExemptionReason.UNSPECIFIED, - regexp: ['(', '/tmp/', 'foo'], - }] - }; - expect(() => { - // tslint:disable-next-line:no-unused-expression - new ConformancePatternRule(config); - }).toThrowError(/Invalid regular expression/); - }); - - it('test memoizer hit', () => { - const config = { - ...baseConfig, - allowlistEntries: [{ - reason: ExemptionReason.UNSPECIFIED, - regexp: [ - `${tmpRegexpForAllowlist}.+[\\\\/]file_0\\.ts`, - ] - }] - }; - const rule = new ConformancePatternRule(config); - // Compile the same file twice to make sure memoizer doesn't - // break things. - let results = compileAndCheck(rule, source); - results = results.concat(compileAndCheck(rule, source)); - - expect(results).toHaveNoFailures(); - }); - - it('test memoizer miss', () => { - const config = { - ...baseConfig, - allowlistEntries: [{ - reason: ExemptionReason.UNSPECIFIED, - regexp: [ - `${tmpRegexpForAllowlist}.+[\\\\/]file_1\\.ts`, - ], - prefix: ['###PrefixNotExist###'], - }] - }; - const rule = new ConformancePatternRule(config); - // Compile the same file twice to make sure memoizer doesn't - // break things. - let results = compileAndCheck(rule, source); - expect(results).toHaveNFailures(1); - - results = compileAndCheck(rule, source); - expect(results).toHaveNFailures(1); - }); -}); - -beforeEach(() => { - jasmine.addMatchers(customMatchers); -});
diff --git a/packages/concatjs/internal/tsetse/util/ast_tools.ts b/packages/concatjs/internal/tsetse/util/ast_tools.ts deleted file mode 100644 index 57505cf..0000000 --- a/packages/concatjs/internal/tsetse/util/ast_tools.ts +++ /dev/null
@@ -1,161 +0,0 @@ -/** - * @fileoverview This is a collection of smaller utility functions to operate on - * a TypeScript AST, used by JSConformance rules and elsewhere. - */ - -import * as ts from 'typescript'; - -/** - * Triggers increased verbosity in the rules. - */ -let DEBUG = false; - -/** - * Turns on or off logging for ConformancePatternRules. - */ -export function setDebug(state: boolean) { - DEBUG = state; -} - -/** - * Debug helper. - */ -export function debugLog(msg: () => string) { - if (DEBUG) { - console.log(msg()); - } -} - -/** - * Returns `n`'s parents in order. - */ -export function parents(n: ts.Node): ts.Node[] { - const p = []; - while (n.parent) { - n = n.parent; - p.push(n); - } - return p; -} - -/** - * Searches for something satisfying the given test in `n` or its children. - */ -export function findInChildren( - n: ts.Node, test: (n: ts.Node) => boolean): boolean { - let toExplore: ts.Node[] = [n]; - let cur: ts.Node|undefined; - while (cur = toExplore.pop()) { - if (test(cur)) { - return true; - } - // Recurse - toExplore = toExplore.concat(cur.getChildren()); - } - return false; -} - -/** - * Returns true if the pattern-based Rule should look at that node and consider - * warning there. The goal is to make it easy to exclude on source files, - * blocks, module declarations, JSDoc, lib.d.ts nodes, that kind of things. - */ -export function shouldExamineNode(n: ts.Node) { - // TODO(b/154674207): Only `isInStockLibraries` seems to be effective here. - // Remove the others. - return !( - ts.isBlock(n) || ts.isModuleBlock(n) || ts.isModuleDeclaration(n) || - ts.isSourceFile(n) || (n.parent && ts.isTypeNode(n.parent)) || - ts.isJSDoc(n) || isInStockLibraries(n)); -} - -/** - * Return whether the given Node is (or is in) a library included as default. - * We currently look for a node_modules/typescript/ prefix, but this could - * be expanded if needed. - */ -export function isInStockLibraries(n: ts.Node|ts.SourceFile): boolean { - const sourceFile = ts.isSourceFile(n) ? n : n.getSourceFile(); - if (sourceFile) { - return sourceFile.fileName.indexOf('node_modules/typescript/') !== -1; - } else { - // the node is nowhere? Consider it as part of the core libs: we can't do - // anything with it anyways, and it was likely included as default. - return true; - } -} - -/** - * Turns the given Symbol into its non-aliased version (which could be itself). - * Returns undefined if given an undefined Symbol (so you can call - * `dealias(typeChecker.getSymbolAtLocation(node))`). - */ -export function dealias( - symbol: ts.Symbol|undefined, tc: ts.TypeChecker): ts.Symbol|undefined { - if (!symbol) { - return undefined; - } - if (symbol.getFlags() & ts.SymbolFlags.Alias) { - // Note: something that has only TypeAlias is not acceptable here. - return dealias(tc.getAliasedSymbol(symbol), tc); - } - return symbol; -} - -/** - * Returns whether `n`'s parents are something indicating a type. - */ -export function isPartOfTypeDeclaration(n: ts.Node) { - return [n, ...parents(n)].some( - p => p.kind === ts.SyntaxKind.TypeReference || - p.kind === ts.SyntaxKind.TypeLiteral); -} - -/** - * Returns whether `n` is under an import statement. - */ -export function isPartOfImportStatement(n: ts.Node) { - return [n, ...parents(n)].some( - p => p.kind === ts.SyntaxKind.ImportDeclaration); -} - -function isAllowlistedNamedDeclaration(n: ts.Node): - n is ts.VariableDeclaration|ts.ClassDeclaration|ts.FunctionDeclaration| - ts.MethodDeclaration|ts.PropertyDeclaration|ts.InterfaceDeclaration| - ts.TypeAliasDeclaration|ts.EnumDeclaration|ts.ModuleDeclaration| - ts.ImportEqualsDeclaration|ts.ExportDeclaration|ts.MissingDeclaration| - ts.ImportClause|ts.ExportSpecifier|ts.ImportSpecifier { - return ts.isVariableDeclaration(n) || ts.isClassDeclaration(n) || - ts.isFunctionDeclaration(n) || ts.isMethodDeclaration(n) || - ts.isPropertyDeclaration(n) || ts.isInterfaceDeclaration(n) || - ts.isTypeAliasDeclaration(n) || ts.isEnumDeclaration(n) || - ts.isModuleDeclaration(n) || ts.isImportEqualsDeclaration(n) || - ts.isExportDeclaration(n) || ts.isMissingDeclaration(n) || - ts.isImportClause(n) || ts.isExportSpecifier(n) || - ts.isImportSpecifier(n); -} - -/** Returns whether `n` is being declared in a declaration statement. */ -export function isNameInDeclaration(n: ts.Node): boolean { - const p = n.parent; - if (p === undefined) return false; - - return (isAllowlistedNamedDeclaration(p) && p.name === n) || - // TODO(pwng) Double-check if these two cases are needed - ts.isVariableDeclarationList(p) || ts.isImportDeclaration(p); -} - -/** - * If verbose, logs the given error that happened while walking n, with a - * stacktrace. - */ -export function logASTWalkError(verbose: boolean, n: ts.Node, e: Error) { - let nodeText = `[error getting name for ${JSON.stringify(n)}]`; - try { - nodeText = '"' + n.getFullText().trim() + '"'; - } catch { - } - debugLog( - () => `Walking node ${nodeText} failed with error ${e}.\n` + - `Stacktrace:\n${e.stack}`); -}
diff --git a/packages/concatjs/internal/tsetse/util/fixer.ts b/packages/concatjs/internal/tsetse/util/fixer.ts deleted file mode 100644 index 75138ba..0000000 --- a/packages/concatjs/internal/tsetse/util/fixer.ts +++ /dev/null
@@ -1,192 +0,0 @@ -import * as ts from 'typescript'; -import {Fix, IndividualChange} from '../failure'; -import {debugLog} from './ast_tools'; - -/** - * A Fixer turns Nodes (that are supposed to have been matched before) into a - * Fix. This is meant to be implemented by Rule implementers (or - * ban-preset-pattern users). See also `buildReplacementFixer` for a simpler way - * of implementing a Fixer. - */ -export interface Fixer { - getFixForFlaggedNode(node: ts.Node): Fix|undefined; -} - -/** - * A simple Fixer builder based on a function that looks at a node, and - * output either nothing, or a replacement. If this is too limiting, implement - * Fixer instead. - */ -export function buildReplacementFixer( - potentialReplacementGenerator: (node: ts.Node) => - ({replaceWith: string} | undefined)): Fixer { - return { - getFixForFlaggedNode: (n: ts.Node): Fix | undefined => { - const partialFix = potentialReplacementGenerator(n); - if (!partialFix) { - return; - } - return { - changes: [{ - sourceFile: n.getSourceFile(), - start: n.getStart(), - end: n.getEnd(), - replacement: partialFix.replaceWith, - }], - }; - } - }; -} - -// TODO(rjamet): Both maybeAddNamedImport and maybeAddNamespacedImport are too -// hard to read to my taste. This could probably be improved upon by being more -// functionnal, to show the filter passes and get rid of the continues and -// returns (which are confusing). - -/** - * Builds an IndividualChange that imports the required symbol from the given - * file under the given name. This might reimport the same thing twice in some - * cases, but it will always make it available under the right name (though - * its name might collide with other imports, as we don't currently check for - * that). - */ -export function maybeAddNamedImport( - source: ts.SourceFile, importWhat: string, fromFile: string, - importAs?: string, tazeComment?: string): IndividualChange|undefined { - const importStatements = source.statements.filter(ts.isImportDeclaration); - const importSpecifier = - importAs ? `${importWhat} as ${importAs}` : importWhat; - - for (const iDecl of importStatements) { - const parsedDecl = maybeParseImportNode(iDecl); - if (!parsedDecl || parsedDecl.fromFile !== fromFile) { - // Not an import from the right file, or couldn't understand the import. - continue; // Jump to the next import. - } - if (ts.isNamespaceImport(parsedDecl.namedBindings)) { - debugLog(() => `... but it's a wildcard import`); - continue; // Jump to the next import. - } - - // Else, bindings is a NamedImports. We can now search whether the right - // symbol is there under the right name. - const foundRightImport = parsedDecl.namedBindings.elements.some( - iSpec => iSpec.propertyName ? - iSpec.name.getText() === importAs && // import {foo as bar} - iSpec.propertyName.getText() === importWhat : - iSpec.name.getText() === importWhat); // import {foo} - - if (foundRightImport) { - debugLog( - () => `"${iDecl.getFullText()}" imports ${importWhat} as we want.`); - return; // Our request is already imported under the right name. - } - - // Else, insert our symbol in the list of imports from that file. - debugLog(() => `No named imports from that file, generating new fix`); - return { - start: parsedDecl.namedBindings.elements[0].getStart(), - end: parsedDecl.namedBindings.elements[0].getStart(), - sourceFile: source, - replacement: `${importSpecifier}, `, - }; - } - - // If we get here, we didn't find anything imported from the wanted file, so - // we'll need the full import string. Add it after the last import, - // and let clang-format handle the rest. - const newImportStatement = `import {${importSpecifier}} from '${fromFile}';` + - (tazeComment ? ` ${tazeComment}\n` : `\n`); - const insertionPosition = importStatements.length ? - importStatements[importStatements.length - 1].getEnd() + 1 : - 0; - return { - start: insertionPosition, - end: insertionPosition, - sourceFile: source, - replacement: newImportStatement, - }; -} - -/** - * Builds an IndividualChange that imports the required namespace from the given - * file under the given name. This might reimport the same thing twice in some - * cases, but it will always make it available under the right name (though - * its name might collide with other imports, as we don't currently check for - * that). - */ -export function maybeAddNamespaceImport( - source: ts.SourceFile, fromFile: string, importAs: string, - tazeComment?: string): IndividualChange|undefined { - const importStatements = source.statements.filter(ts.isImportDeclaration); - - const hasTheRightImport = importStatements.some(iDecl => { - const parsedDecl = maybeParseImportNode(iDecl); - if (!parsedDecl || parsedDecl.fromFile !== fromFile) { - // Not an import from the right file, or couldn't understand the import. - return false; - } - debugLog(() => `"${iDecl.getFullText()}" is an import from the right file`); - - if (ts.isNamedImports(parsedDecl.namedBindings)) { - debugLog(() => `... but it's a named import`); - return false; // irrelevant to our namespace imports - } - // Else, bindings is a NamespaceImport. - if (parsedDecl.namedBindings.name.getText() !== importAs) { - debugLog(() => `... but not the right name, we need to reimport`); - return false; - } - debugLog(() => `... and the right name, no need to reimport`); - return true; - }); - - if (!hasTheRightImport) { - const insertionPosition = importStatements.length ? - importStatements[importStatements.length - 1].getEnd() + 1 : - 0; - return { - start: insertionPosition, - end: insertionPosition, - sourceFile: source, - replacement: tazeComment ? - `import * as ${importAs} from '${fromFile}'; ${tazeComment}\n` : - `import * as ${importAs} from '${fromFile}';\n`, - }; - } - return; -} - -/** - * This tries to make sense of an ImportDeclaration, and returns the interesting - * parts, undefined if the import declaration is valid but not understandable by - * the checker. - */ -function maybeParseImportNode(iDecl: ts.ImportDeclaration): { - namedBindings: ts.NamedImportBindings|ts.NamespaceImport, - fromFile: string -}|undefined { - if (!iDecl.importClause) { - // something like import "./file"; - debugLog( - () => - `Ignoring import without imported symbol: ${iDecl.getFullText()}`); - return; - } - if (iDecl.importClause.name || !iDecl.importClause.namedBindings) { - // Seems to happen in defaults imports like import Foo from 'Bar'. - // Not much we can do with that when trying to get a hold of some symbols, - // so just ignore that line (worst case, we'll suggest another import - // style). - debugLog(() => `Ignoring import: ${iDecl.getFullText()}`); - return; - } - if (!ts.isStringLiteral(iDecl.moduleSpecifier)) { - debugLog(() => `Ignoring import whose module specifier is not literal`); - return; - } - return { - namedBindings: iDecl.importClause.namedBindings, - fromFile: iDecl.moduleSpecifier.text - }; -}
diff --git a/packages/concatjs/internal/tsetse/util/fixer_test.ts b/packages/concatjs/internal/tsetse/util/fixer_test.ts deleted file mode 100644 index 470ffce..0000000 --- a/packages/concatjs/internal/tsetse/util/fixer_test.ts +++ /dev/null
@@ -1,228 +0,0 @@ -import 'jasmine'; -import * as ts from 'typescript'; -import {Failure, Fix} from '../failure'; -import {ConformancePatternRule, ErrorCode, PatternKind} from '../rules/conformance_pattern_rule'; -import {buildReplacementFixer, Fixer, maybeAddNamedImport, maybeAddNamespaceImport} from './fixer'; -import {compile, compileAndCheck, customMatchers} from './testing/test_support'; - -const uppercaseFixer: Fixer = { - getFixForFlaggedNode(node: ts.Node): Fix { - return { - changes: [{ - start: node.getStart(), - end: node.getEnd(), - replacement: node.getText().toUpperCase(), - sourceFile: node.getSourceFile(), - }] - }; - } -}; - -const uppercaseFixerBuilt: Fixer = buildReplacementFixer((node: ts.Node) => { - return {replaceWith: node.getText().toUpperCase()}; -}); - -// The initial config and source off which we run those checks. -const baseConfig = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'found citation', - kind: PatternKind.BANNED_PROPERTY_WRITE, - values: ['HTMLQuoteElement.prototype.cite'], -}; - -const source = `export {};\n` + - `const q = document.createElement('q');\n` + - `q.cite = 'some example string';\n`; - -describe('ConformancePatternRule\'s fixer', () => { - describe('Generates basic fixes', () => { - it('for a single match', () => { - const rule = new ConformancePatternRule(baseConfig, uppercaseFixer); - const results = compileAndCheck(rule, source); - - expect(results).toHaveFailuresMatching({ - matchedCode: `q.cite = 'some example string'`, - messageText: 'found citation', - fix: [ - {start: 50, end: 80, replacement: `Q.CITE = 'SOME EXAMPLE STRING'`} - ] - }); - }); - - it('for a single match (alternate fixer)', () => { - const rule = new ConformancePatternRule(baseConfig, uppercaseFixerBuilt); - const results = compileAndCheck(rule, source); - - expect(results).toHaveFailuresMatching({ - matchedCode: `q.cite = 'some example string'`, - messageText: 'found citation', - fix: [ - {start: 50, end: 80, replacement: `Q.CITE = 'SOME EXAMPLE STRING'`} - ] - }); - }); - - it('for several matches', () => { - const rule = new ConformancePatternRule(baseConfig, uppercaseFixer); - const sourceTwoMatches = - source + `q.cite = 'some other example string';\n`; - const results = compileAndCheck(rule, sourceTwoMatches); - - expect(results).toHaveFailuresMatching( - { - matchedCode: `q.cite = 'some example string'`, - messageText: 'found citation', - fix: [{ - start: 50, - end: 80, - replacement: `Q.CITE = 'SOME EXAMPLE STRING'` - }] - }, - { - matchedCode: `q.cite = 'some other example string'`, - messageText: 'found citation', - fix: [{ - start: 82, - end: 118, - replacement: `Q.CITE = 'SOME OTHER EXAMPLE STRING'` - }] - }); - - expect(results[0].fixToReadableStringInContext()) - .toBe( - `Suggested fix:\n` + - `- Replace the full match with: Q.CITE = 'SOME EXAMPLE STRING'`); - expect(results[1].fixToReadableStringInContext()) - .toBe( - `Suggested fix:\n` + - `- Replace the full match with: Q.CITE = 'SOME OTHER EXAMPLE STRING'`); - }); - }); - - describe('adds imports', () => { - const addNamedImportFixer: Fixer = { - getFixForFlaggedNode(n: ts.Node): Fix | - undefined { - const changes = []; - const ic1 = - maybeAddNamedImport(n.getSourceFile(), 'foo', './file_1', 'bar'); - if (ic1) { - changes.push(ic1); - } - const ic2 = - maybeAddNamedImport(n.getSourceFile(), 'foo2', './file_2', 'bar2'); - if (ic2) { - changes.push(ic2); - } - return changes.length ? {changes} : undefined; - } - }; - - it('maybeAddNamedImport additions', () => { - const results = compileAndCheck( - new ConformancePatternRule(baseConfig, addNamedImportFixer), source); - - expect(results[0]).toHaveFixMatching([ - { - start: 0, - end: 0, - replacement: `import {foo as bar} from './file_1';\n` - }, - { - start: 0, - end: 0, - replacement: `import {foo2 as bar2} from './file_2';\n` - } - ]); - expect(results[0].fixToReadableStringInContext()) - .toBe( - `Suggested fix:\n` + - `- Add new import: import {foo as bar} from './file_1';\n` + - `- Add new import: import {foo2 as bar2} from './file_2';`); - }); - - it('maybeAddNamedImport already there', () => { - const results = compileAndCheck( - new ConformancePatternRule(baseConfig, addNamedImportFixer), - 'import {foo as bar} from \'./file_1\';\n' + source, - 'export const foo = 1;'); - - expect(results[0]).toHaveFixMatching([{ - start: 37, - end: 37, - replacement: `import {foo2 as bar2} from './file_2';\n` - }]); - expect(results[0].fixToReadableStringInContext()) - .toBe( - `Suggested fix:\n` + - `- Add new import: import {foo2 as bar2} from './file_2';`); - }); - - it('maybeAddNamedImport different name', () => { - const results = compileAndCheck( - new ConformancePatternRule(baseConfig, addNamedImportFixer), - 'import {foo as baz} from \'./file_1\';\n' + source, - 'export const foo = 1;'); - - expect(results[0]).toHaveFixMatching([ - {start: 8, end: 8, replacement: `foo as bar, `}, { - start: 37, - end: 37, - replacement: `import {foo2 as bar2} from './file_2';\n` - } - ]); - expect(results[0].fixToReadableStringInContext()) - .toBe( - `Suggested fix:\n` + - `- Insert at line 1, char 9: foo as bar,\n` + - `- Add new import: import {foo2 as bar2} from './file_2';`); - }); - - it('maybeAddNamespacedImport', () => { - const addNamespacedImportFixer: Fixer = { - getFixForFlaggedNode(n: ts.Node): Fix | - undefined { - const ic = - maybeAddNamespaceImport(n.getSourceFile(), './file_1', 'foo'); - if (ic) return {changes: [ic]}; - return; - } - }; - const results = compileAndCheck( - new ConformancePatternRule(baseConfig, addNamespacedImportFixer), - source); - - expect(results[0]).toHaveFixMatching([ - {start: 0, end: 0, replacement: `import * as foo from './file_1';\n`} - ]); - }); - }); - - describe('the logic for location->text transforms', () => { - const sourceFile = compile(`let a;\nlet b;\n`) - .getSourceFiles() - .filter(f => f.fileName.indexOf('file_0') !== -1)[0]; - // let a;\nlet b;\n - // 0123456 7890123 Positions - // 1234567 1234567 Expected result in characters - - it('stringifies as expected', () => { - // Only the sourceFile matters here. - const failure = new Failure(sourceFile, NaN, NaN, 'whatever', NaN); - - expect(failure.readableRange(0, 0)).toBe('at line 1, char 1'); - expect(failure.readableRange(1, 1)).toBe('at line 1, char 2'); - expect(failure.readableRange(0, 1)).toBe('line 1, from char 1 to 2'); - expect(failure.readableRange(0, 1)).toBe('line 1, from char 1 to 2'); - expect(failure.readableRange(7, 7)).toBe('at line 2, char 1'); - expect(failure.readableRange(0, 7)) - .toBe('from line 1, char 1 to line 2, char 1'); - }); - }); -}); - - - -beforeEach(() => { - jasmine.addMatchers(customMatchers); -});
diff --git a/packages/concatjs/internal/tsetse/util/is_literal.ts b/packages/concatjs/internal/tsetse/util/is_literal.ts deleted file mode 100644 index 2f44ce2..0000000 --- a/packages/concatjs/internal/tsetse/util/is_literal.ts +++ /dev/null
@@ -1,108 +0,0 @@ -import * as ts from 'typescript'; -import {findInChildren} from './ast_tools'; - -/** - * Determines if the given ts.Node is literal enough for security purposes. This - * is true when the value is built from compile-time constants, with a certain - * tolerance for indirection in order to make this more user-friendly. - * - * This considers a few different things. We accept - * - What TS deems literal (literal strings, literal numbers, literal booleans, - * enum literals), - * - Binary operations of two expressions that we accept (including - * concatenation), - * - Template interpolations of what we accept, - * - `x?y:z` constructions, if we accept `y` and `z` - * - Variables that are const, and initialized with an expression we accept - * - * And to prevent bypasses, expressions that include casts are not accepted, and - * this checker does not follow imports. - */ -export function isLiteral(typeChecker: ts.TypeChecker, node: ts.Node): boolean { - if (ts.isBinaryExpression(node) && - node.operatorToken.kind === ts.SyntaxKind.PlusToken) { - // Concatenation is fine, if the parts are literals. - return ( - isLiteral(typeChecker, node.left) && - isLiteral(typeChecker, node.right)); - } else if (ts.isTemplateExpression(node)) { - // Same for template expressions. - return node.templateSpans.every(span => { - return isLiteral(typeChecker, span.expression); - }); - } else if (ts.isTemplateLiteral(node)) { - // and literals (in that order). - return true; - } else if (ts.isConditionalExpression(node)) { - return isLiteral(typeChecker, node.whenTrue) && - isLiteral(typeChecker, node.whenFalse); - } else if (ts.isIdentifier(node)) { - return isUnderlyingValueAStringLiteral(node, typeChecker); - } - - const hasCasts = findInChildren(node, ts.isAsExpression); - - return !hasCasts && isLiteralAccordingToItsType(typeChecker, node); -} - -/** - * Given an identifier, this function goes around the AST to determine - * whether we should consider it a string literal, on a best-effort basis. It - * is an approximation, but should never have false positives. - */ -function isUnderlyingValueAStringLiteral( - identifier: ts.Identifier, tc: ts.TypeChecker) { - // The identifier references a value, and we try to follow the trail: if we - // find a variable declaration for the identifier, and it was declared as a - // const (so we know it wasn't altered along the way), then the value used - // in the declaration is the value our identifier references. That means we - // should look at the value used in its initialization (by applying the same - // rules as before). - // Since we're best-effort, if a part of that operation failed due to lack - // of support (for instance, the identifier was imported), then we fail - // closed and don't consider the value a literal. - - // TODO(rjamet): This doesn't follow imports, which is a feature that we need - // in a fair amount of cases. - return getVariableDeclarationsInSameFile(identifier, tc) - .filter(isConst) - .some(d => d.initializer !== undefined && isLiteral(tc, d.initializer)); -} - -/** - * Returns whether this thing is a literal based on TS's understanding. This is - * only looking at the local type, so there's no magic in that function. - */ -function isLiteralAccordingToItsType( - typeChecker: ts.TypeChecker, node: ts.Node): boolean { - const nodeType = typeChecker.getTypeAtLocation(node); - return (nodeType.flags & - (ts.TypeFlags.StringLiteral | ts.TypeFlags.NumberLiteral | - ts.TypeFlags.BooleanLiteral | ts.TypeFlags.EnumLiteral)) !== 0; -} - -/** - * Follows the symbol behind the given identifier, assuming it is a variable, - * and return all the variable declarations we can find that match it in the - * same file. - */ -function getVariableDeclarationsInSameFile( - node: ts.Identifier, tc: ts.TypeChecker): ts.VariableDeclaration[] { - const symbol = tc.getSymbolAtLocation(node); - if (!symbol) { - return []; - } - const decls = symbol.getDeclarations(); - if (!decls) { - return []; - } - return decls.filter(ts.isVariableDeclaration); -} - -// Tests whether the given variable declaration is Const. -function isConst(varDecl: ts.VariableDeclaration): boolean { - return Boolean( - varDecl && varDecl.parent && - ts.isVariableDeclarationList(varDecl.parent) && - varDecl.parent.flags & ts.NodeFlags.Const); -}
diff --git a/packages/concatjs/internal/tsetse/util/is_literal_test.ts b/packages/concatjs/internal/tsetse/util/is_literal_test.ts deleted file mode 100644 index 92b49d6..0000000 --- a/packages/concatjs/internal/tsetse/util/is_literal_test.ts +++ /dev/null
@@ -1,69 +0,0 @@ -import 'jasmine'; -import {isInStockLibraries} from './ast_tools'; -import {isLiteral} from './is_literal'; -import {compile} from './testing/test_support'; - -describe('isLiteral', () => { - // Different platforms pull in different files (or from different - // paths) when compiling a .ts file. We only want to inspect source - // defined in the tests. This pattern is determinted by `compile`. - const testSrcNamePattern = /file_\d+.ts$/; - - it('understands constants', () => { - // Keep these to single-expression programs. - const constantExpressionsSources = [ - `'hello'`, - `'hello' + 'hi'`, - `1`, - `1 + 1`, - '`abcdef`', - '`abcd${"ef"}`', - '`abcd${1+1}`+`hi`', - `1 ? 'hi' : 'hello'`, - `window.name ? 'hi' : 'hello'`, - ]; - - // We don't bother with a rule for this one. - const constantProgram = compile(...constantExpressionsSources); - const constantCompiledSources = constantProgram.getSourceFiles(); - - const constantExpressions = - constantCompiledSources.filter(s => testSrcNamePattern.test(s.fileName)) - .map(s => s.statements[0].getChildren()[0]); - - const constantTc = constantProgram.getTypeChecker(); - for (const expr of constantExpressions) { - expect(isLiteral(constantTc, expr)) - .toBe( - true, - `Expected "${expr.getFullText()}" to be considered constant.`); - } - }); - - it('understands non-constants', () => { - const nonconstantExpressionsSources = [ - `window.name`, - `'hello' + window.name`, - `window.name + 'hello'`, - '`abcd${window.name}`', - `1 ? window.name : 'hello'`, - `1 ? 'hello' : window.name`, - ]; - - const nonconstantProgram = compile(...nonconstantExpressionsSources); - const nonconstantCompiledSources = nonconstantProgram.getSourceFiles(); - const nonconstantTc = nonconstantProgram.getTypeChecker(); - const nonconstantExpressions = - nonconstantCompiledSources - .filter(s => testSrcNamePattern.test(s.fileName)) - .map(s => s.statements[0].getChildren()[0]); - - for (const expr of nonconstantExpressions) { - expect(isLiteral(nonconstantTc, expr)) - .toBe( - false, - `Expected "${ - expr.getFullText()}" not to be considered constant.`); - } - }); -});
diff --git a/packages/concatjs/internal/tsetse/util/pattern_config.ts b/packages/concatjs/internal/tsetse/util/pattern_config.ts deleted file mode 100644 index 7d0aa39..0000000 --- a/packages/concatjs/internal/tsetse/util/pattern_config.ts +++ /dev/null
@@ -1,58 +0,0 @@ -import {AllowlistEntry} from './allowlist'; - -/** - * The list of supported patterns useable in ConformancePatternRule. The - * patterns whose name match JSConformance patterns should behave similarly (see - * https://github.com/google/closure-compiler/wiki/JS-Conformance-Framework) - */ -export enum PatternKind { - /** Ban use of fully distinguished names. */ - BANNED_NAME = 'banned-name', - /** Ban use of instance properties */ - BANNED_PROPERTY = 'banned-property', - /** - * Ban instance property, like BANNED_PROPERTY but where reads of the - * property are allowed. - */ - BANNED_PROPERTY_WRITE = 'banned-property-write', - /** - * Ban instance property write unless the property is assigned a constant - * literal. - */ - BANNED_PROPERTY_NON_CONSTANT_WRITE = 'banned-property-non-constant-write', -} - -/** - * A config for `PatternEngine`. - */ -export interface PatternEngineConfig { - /** - * Values have a pattern-specific syntax. - * - * TODO(rjamet): We'll document them, but for now see each patternKind's - * tests for examples. - */ - values: string[]; - - /** The error code assigned to this pattern. */ - errorCode: number; - - /** The error message this pattern will create. */ - errorMessage: string; - - /** A list of allowlist blocks. */ - allowlistEntries?: AllowlistEntry[]; -} - -/** - * A config for `ConformancePatternRule`. - */ -export interface PatternRuleConfig extends PatternEngineConfig { - kind: PatternKind; - - /** - * An optional name for that rule, which will be the rule's `ruleName`. - * Should be lower-dashed-case. - */ - name?: string; -}
diff --git a/packages/concatjs/internal/tsetse/util/pattern_engines/name_engine.ts b/packages/concatjs/internal/tsetse/util/pattern_engines/name_engine.ts deleted file mode 100644 index 27c97c2..0000000 --- a/packages/concatjs/internal/tsetse/util/pattern_engines/name_engine.ts +++ /dev/null
@@ -1,37 +0,0 @@ -import * as ts from 'typescript'; -import {Checker} from '../../checker'; -import {ErrorCode} from '../../error_code'; -import {AbsoluteMatcher} from '../absolute_matcher'; -import {debugLog} from '../ast_tools'; -import {Fixer} from '../fixer'; -import {PatternEngine} from './pattern_engine'; - -function checkId( - tc: ts.TypeChecker, n: ts.Identifier, - matcher: AbsoluteMatcher): ts.Identifier|undefined { - debugLog(() => `inspecting ${n.getText().trim()}`); - if (!matcher.matches(n, tc)) { - debugLog(() => 'Not the right global name.'); - return; - } - return n; -} - -/** Engine for the BANNED_NAME pattern */ -export class NameEngine extends PatternEngine { - register(checker: Checker) { - for (const value of this.config.values) { - const matcher = new AbsoluteMatcher(value); - - // `String.prototype.split` only returns emtpy array when both the string - // and the splitter are empty. Here we should be able to safely assert pop - // returns a non-null result. - const bannedIdName = matcher.bannedName.split('.').pop()!; - checker.onNamedIdentifier( - bannedIdName, - this.wrapCheckWithAllowlistingAndFixer( - (tc, n: ts.Identifier) => checkId(tc, n, matcher)), - this.config.errorCode); - } - } -}
diff --git a/packages/concatjs/internal/tsetse/util/pattern_engines/name_engine_test.ts b/packages/concatjs/internal/tsetse/util/pattern_engines/name_engine_test.ts deleted file mode 100644 index ce39875..0000000 --- a/packages/concatjs/internal/tsetse/util/pattern_engines/name_engine_test.ts +++ /dev/null
@@ -1,62 +0,0 @@ -import 'jasmine'; -import {ConformancePatternRule, ErrorCode, PatternKind} from '../../rules/conformance_pattern_rule'; -import {compileAndCheck, customMatchers} from '../../util/testing/test_support'; - -describe('BANNED_NAME', () => { - it('matches simple example of globals', () => { - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'no Infinity', - kind: PatternKind.BANNED_NAME, - values: ['GLOBAL|Infinity'] - }; - const source = `Infinity; 1+1;`; - const results = compileAndCheck(new ConformancePatternRule(config), source); - - expect(results).toHaveFailuresMatching( - {matchedCode: `Infinity`, messageText: 'no Infinity'}); - }); - - it('matches namespaced globals', () => { - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'no blob url', - kind: PatternKind.BANNED_NAME, - values: ['GLOBAL|URL.createObjectURL'] - }; - const source = `URL.createObjectURL({});`; - const results = compileAndCheck(new ConformancePatternRule(config), source); - - expect(results).toHaveFailuresMatching( - {matchedCode: `createObjectURL`, messageText: 'no blob url'}); - }); - - it('does not choke on type aliases', () => { - // This test case checks that we do not regress on the AbsoluteMatcher's - // handling of type aliases. In dealias, from utils/ast_tools.ts, the - // typechecker's getAliasedSymbol function should only be called with - // Symbols that verify ts.SymbolFlags.Alias, and ts.SymbolFlags.TypeAlias is - // not acceptable (the typechecker will throw). - - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'should not trigger', - kind: PatternKind.BANNED_NAME, - values: ['ANY_SYMBOL|whatever'] - }; - const sources = [ - `export type Foo = {bar: number, baz: (x:string)=>void}`, - `import {Foo} from './file_0'; - export const c: Foo["baz"] = (x:string)=>{};`, - `import {c} from './file_1'; c(window.name);` - ]; - const results = - compileAndCheck(new ConformancePatternRule(config), ...sources); - - expect(results).toHaveNoFailures(); - }); -}); - -beforeEach(() => { - jasmine.addMatchers(customMatchers); -});
diff --git a/packages/concatjs/internal/tsetse/util/pattern_engines/pattern_engine.ts b/packages/concatjs/internal/tsetse/util/pattern_engines/pattern_engine.ts deleted file mode 100644 index df15ab5..0000000 --- a/packages/concatjs/internal/tsetse/util/pattern_engines/pattern_engine.ts +++ /dev/null
@@ -1,54 +0,0 @@ -import * as path from 'path'; -import * as ts from 'typescript'; - -import {Checker} from '../../checker'; -import {Fix} from '../../failure'; -import {Allowlist} from '../../util/allowlist'; -import {Fixer} from '../../util/fixer'; -import {PatternEngineConfig} from '../../util/pattern_config'; -import {shouldExamineNode} from '../ast_tools'; - -/** - * A patternEngine is the logic that handles a specific PatternKind. - */ -export abstract class PatternEngine { - private readonly allowlist: Allowlist; - - constructor( - protected readonly config: PatternEngineConfig, - protected readonly fixer?: Fixer) { - this.allowlist = new Allowlist(config.allowlistEntries); - } - - /** - * `register` will be called by the ConformanceRule to tell Tsetse the - * PatternEngine will handle matching. Implementations should use - * `checkAndFilterResults` as a wrapper for `check`. - */ - abstract register(checker: Checker): void; - - /** - * A composer that wraps checking functions with code handling aspects of the - * analysis that are not engine-specific, and which defers to the - * subclass-specific logic afterwards. Subclasses should transform their - * checking logic with this composer before registered on the checker. - */ - protected wrapCheckWithAllowlistingAndFixer<T extends ts.Node>( - checkFunction: (tc: ts.TypeChecker, n: T) => ts.Node | - undefined): (c: Checker, n: T) => void { - return (c: Checker, n: T) => { - const sf = n.getSourceFile(); - if (!shouldExamineNode(n) || sf.isDeclarationFile) { - return; - } - const matchedNode = checkFunction(c.typeChecker, n); - if (matchedNode && - !this.allowlist.isAllowlisted(path.resolve(sf.fileName))) { - const fix: Fix|undefined = this.fixer ? - this.fixer.getFixForFlaggedNode(matchedNode) : - undefined; - c.addFailureAtNode(matchedNode, this.config.errorMessage, fix); - } - } - } -}
diff --git a/packages/concatjs/internal/tsetse/util/pattern_engines/property_engine.ts b/packages/concatjs/internal/tsetse/util/pattern_engines/property_engine.ts deleted file mode 100644 index 5cfc7af..0000000 --- a/packages/concatjs/internal/tsetse/util/pattern_engines/property_engine.ts +++ /dev/null
@@ -1,46 +0,0 @@ -import * as ts from 'typescript'; - -import {Checker} from '../../checker'; -import {ErrorCode} from '../../error_code'; -import {debugLog} from '../ast_tools'; -import {Fixer} from '../fixer'; -import {PropertyMatcher} from '../property_matcher'; - -import {PatternEngine} from './pattern_engine'; - -/** Match an AST node with a property matcher. */ -export function matchProperty( - tc: ts.TypeChecker, - n: ts.PropertyAccessExpression|ts.ElementAccessExpression, - matcher: PropertyMatcher): ts.Node|undefined { - debugLog(() => `inspecting ${n.getText().trim()}`); - if (!matcher.typeMatches(tc.getTypeAtLocation(n.expression))) return; - return n; -} - -/** - * Engine for the BANNED_PROPERTY pattern. It captures accesses to property - * matching the spec regardless whether it's a read or write. - */ -export class PropertyEngine extends PatternEngine { - protected registerWith(checker: Checker, matchNode: typeof matchProperty) { - for (const value of this.config.values) { - const matcher = PropertyMatcher.fromSpec(value); - checker.onNamedPropertyAccess( - matcher.bannedProperty, - this.wrapCheckWithAllowlistingAndFixer( - (tc, n) => matchNode(tc, n, matcher)), - this.config.errorCode); - - checker.onStringLiteralElementAccess( - matcher.bannedProperty, - this.wrapCheckWithAllowlistingAndFixer( - (tc, n) => matchNode(tc, n, matcher)), - this.config.errorCode); - } - } - - register(checker: Checker) { - this.registerWith(checker, matchProperty); - } -}
diff --git a/packages/concatjs/internal/tsetse/util/pattern_engines/property_engine_test.ts b/packages/concatjs/internal/tsetse/util/pattern_engines/property_engine_test.ts deleted file mode 100644 index 121e5e6..0000000 --- a/packages/concatjs/internal/tsetse/util/pattern_engines/property_engine_test.ts +++ /dev/null
@@ -1,45 +0,0 @@ -import 'jasmine'; - -import {ConformancePatternRule, ErrorCode, PatternKind} from '../../rules/conformance_pattern_rule'; -import {compileAndCheck, customMatchers} from '../testing/test_support'; - -describe('BANNED_PROPERTY', () => { - beforeEach(() => { - jasmine.addMatchers(customMatchers); - }); - - it('matches a trivial example', () => { - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'No Location#href access', - kind: PatternKind.BANNED_PROPERTY, - values: ['Location.prototype.href'], - }; - const source = `const href = location.href + location['href'];`; - const results = compileAndCheck(new ConformancePatternRule(config), source); - - expect(results).toHaveFailuresMatching( - { - matchedCode: 'location.href', - messageText: 'No Location#href access', - }, - { - matchedCode: `location['href']`, - messageText: 'No Location#href access', - }); - }); - - it('matches element access expressions with string literal types', () => { - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'No Location#href access', - kind: PatternKind.BANNED_PROPERTY, - values: ['Location.prototype.href'], - }; - const source = `declare const key: 'href'; const href = location[key];`; - const results = compileAndCheck(new ConformancePatternRule(config), source); - - expect(results).toHaveFailuresMatching( - {matchedCode: 'location[key]', messageText: 'No Location#href access'}); - }); -});
diff --git a/packages/concatjs/internal/tsetse/util/pattern_engines/property_non_constant_write_engine.ts b/packages/concatjs/internal/tsetse/util/pattern_engines/property_non_constant_write_engine.ts deleted file mode 100644 index d8f2f13..0000000 --- a/packages/concatjs/internal/tsetse/util/pattern_engines/property_non_constant_write_engine.ts +++ /dev/null
@@ -1,37 +0,0 @@ -import * as ts from 'typescript'; - -import {Checker} from '../../checker'; -import {ErrorCode} from '../../error_code'; -import {debugLog} from '../ast_tools'; -import {Fixer} from '../fixer'; -import {isLiteral} from '../is_literal'; -import {PropertyMatcher} from '../property_matcher'; - -import {matchPropertyWrite, PropertyWriteEngine} from './property_write_engine'; - -function matchPropertyNonConstantWrite( - tc: ts.TypeChecker, - n: ts.PropertyAccessExpression|ts.ElementAccessExpression, - matcher: PropertyMatcher): ts.Node|undefined { - debugLog(() => `inspecting ${n.getFullText().trim()}`); - if (matchPropertyWrite(tc, n, matcher) === undefined) { - return; - } - const rval = (n.parent as ts.BinaryExpression).right; - if (isLiteral(tc, rval)) { - debugLog( - () => `Assigned value (${ - rval.getFullText()}) is a compile-time constant.`); - return; - } - return n.parent; -} - -/** - * The engine for BANNED_PROPERTY_NON_CONSTANT_WRITE. - */ -export class PropertyNonConstantWriteEngine extends PropertyWriteEngine { - register(checker: Checker) { - this.registerWith(checker, matchPropertyNonConstantWrite); - } -}
diff --git a/packages/concatjs/internal/tsetse/util/pattern_engines/property_non_constant_write_engine_test.ts b/packages/concatjs/internal/tsetse/util/pattern_engines/property_non_constant_write_engine_test.ts deleted file mode 100644 index 6d2f4fa..0000000 --- a/packages/concatjs/internal/tsetse/util/pattern_engines/property_non_constant_write_engine_test.ts +++ /dev/null
@@ -1,37 +0,0 @@ -import 'jasmine'; -import {ConformancePatternRule, ErrorCode, PatternKind} from '../../rules/conformance_pattern_rule'; -import {compileAndCheck, customMatchers} from '../../util/testing/test_support'; - -describe('BANNED_PROPERTY_NON_CONSTANT_WRITE', () => { - it('matches a trivial example', () => { - const source = [ - `const q = document.createElement('q');`, - `q.cite = 'some example string';`, - `q['cite'] = 'some example string';`, - `q.cite = window.name;`, - `q['cite'] = window.name;`, - ].join('\n'); - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'do not cite dynamically', - kind: PatternKind.BANNED_PROPERTY_NON_CONSTANT_WRITE, - values: ['HTMLQuoteElement.prototype.cite'] - }; - const results = compileAndCheck(new ConformancePatternRule(config), source); - - expect(results).toHaveFailuresMatching( - { - matchedCode: `q.cite = window.name`, - messageText: 'do not cite dynamically', - }, - { - matchedCode: `q['cite'] = window.name`, - messageText: 'do not cite dynamically', - }); - }); -}); - - -beforeEach(() => { - jasmine.addMatchers(customMatchers); -});
diff --git a/packages/concatjs/internal/tsetse/util/pattern_engines/property_write_engine.ts b/packages/concatjs/internal/tsetse/util/pattern_engines/property_write_engine.ts deleted file mode 100644 index b640ce3..0000000 --- a/packages/concatjs/internal/tsetse/util/pattern_engines/property_write_engine.ts +++ /dev/null
@@ -1,36 +0,0 @@ -import * as ts from 'typescript'; - -import {Checker} from '../../checker'; -import {ErrorCode} from '../../error_code'; -import {debugLog} from '../ast_tools'; -import {Fixer} from '../fixer'; -import {PropertyMatcher} from '../property_matcher'; - -import {matchProperty, PropertyEngine} from './property_engine'; - -/** Test if an AST node is a matched property write. */ -export function matchPropertyWrite( - tc: ts.TypeChecker, - n: ts.PropertyAccessExpression|ts.ElementAccessExpression, - matcher: PropertyMatcher): ts.BinaryExpression|undefined { - debugLog(() => `inspecting ${n.parent.getText().trim()}`); - - if (matchProperty(tc, n, matcher) === undefined) return; - - const assignment = n.parent; - - if (!ts.isBinaryExpression(assignment)) return; - if (assignment.operatorToken.kind !== ts.SyntaxKind.EqualsToken) return; - if (assignment.left !== n) return; - - return assignment; -} - -/** - * The engine for BANNED_PROPERTY_WRITE. - */ -export class PropertyWriteEngine extends PropertyEngine { - register(checker: Checker) { - this.registerWith(checker, matchPropertyWrite); - } -}
diff --git a/packages/concatjs/internal/tsetse/util/pattern_engines/property_write_engine_test.ts b/packages/concatjs/internal/tsetse/util/pattern_engines/property_write_engine_test.ts deleted file mode 100644 index eec8771..0000000 --- a/packages/concatjs/internal/tsetse/util/pattern_engines/property_write_engine_test.ts +++ /dev/null
@@ -1,137 +0,0 @@ -import 'jasmine'; -import {ConformancePatternRule, ErrorCode, PatternKind} from '../../rules/conformance_pattern_rule'; -import {compileAndCheck, customMatchers} from '../testing/test_support'; - -describe('BANNED_PROPERTY_WRITE', () => { - describe('simple matcher tests', () => { - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'do not cite', - kind: PatternKind.BANNED_PROPERTY_WRITE, - values: ['HTMLQuoteElement.prototype.cite'] - }; - const rule = new ConformancePatternRule(config); - - it('matches simple examples', () => { - const source = [ - `const q = document.createElement('q');`, - `q.cite = 'some example string';`, - `q['cite'] = 'some example string';`, - ].join('\n'); - const results = compileAndCheck(rule, source); - - expect(results).toHaveFailuresMatching( - { - matchedCode: `q.cite = 'some example string'`, - messageText: 'do not cite' - }, - { - matchedCode: `q['cite'] = 'some example string'`, - messageText: 'do not cite' - }); - }); - - it('matches precisely, even with whitespace or comments', () => { - const source = [ - `const q = document.createElement('q');`, - ` q.cite = 'exampleA';`, - `q.cite = 'exampleB' ;`, - `/* test1 */ q.cite = /* test2 */ 'exampleC' /* test3 */;`, - ].join('\n'); - const results = compileAndCheck(rule, source); - - expect(results).toHaveFailuresMatching( - {matchedCode: `q.cite = 'exampleA'`, messageText: 'do not cite'}, - {matchedCode: `q.cite = 'exampleB'`, messageText: 'do not cite'}, { - matchedCode: `q.cite = /* test2 */ 'exampleC'`, - messageText: 'do not cite' - }); - }); - - it('understands function prototypes', () => { - const source = [ - `function foo(q:HTMLQuoteElement) {`, - ` q.cite = 'some example string';`, - `}`, - ].join('\n'); - const results = compileAndCheck(rule, source); - - expect(results).toHaveFailuresMatching({ - matchedCode: `q.cite = 'some example string'`, - messageText: 'do not cite' - }); - }); - - it('understands imported symbols', () => { - const sources = [ - `const q = document.createElement('q'); export {q};`, - `import {q} from './file_0'; q.cite = window.name;` - ]; - const results = compileAndCheck(rule, ...sources); - - expect(results).toHaveFailuresMatching({ - matchedCode: 'q.cite = window.name', - fileName: 'file_1.ts', - messageText: 'do not cite', - }); - }); - - it('understands shadowing', () => { - const source = [ - `const q = document.createElement('q');`, - `const f1 = (q: {cite: string}) => { q.cite = 'example 1'; };`, - ].join('\n'); - const rule = new ConformancePatternRule(config); - const results = compileAndCheck(rule, source); - - expect(results).toHaveNoFailures(); - }); - }); - - - describe('with inheritance', () => { - const source = [ - `class Parent { x: number }`, - `class Child extends Parent {}`, - `const c: Child = new Child();`, - `c.x = 1;`, - ].join('\n'); - - // Both of these should have the same results: in `c.x`, `x` matches, - // and `c` is both a Parent and a Child. - const expectedFailure = { - matchedCode: 'c.x = 1', - messageText: 'found write to x', - }; - - it('banning Parent.x matches (instance of Child).x', () => { - const configOnParent = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'found write to x', - kind: PatternKind.BANNED_PROPERTY_WRITE, - values: ['Parent.prototype.x'] - }; - const ruleOnParent = new ConformancePatternRule(configOnParent); - const results = compileAndCheck(ruleOnParent, source); - - expect(results).toHaveFailuresMatching(expectedFailure); - }); - - it('banning Child.x matches x defined on Parent', () => { - const configOnChild = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'found write to x', - kind: PatternKind.BANNED_PROPERTY_WRITE, - values: ['Child.prototype.x'] - }; - const ruleOnChild = new ConformancePatternRule(configOnChild); - const results = compileAndCheck(ruleOnChild, source); - - expect(results).toHaveFailuresMatching(expectedFailure); - }); - }); -}); - -beforeEach(() => { - jasmine.addMatchers(customMatchers); -});
diff --git a/packages/concatjs/internal/tsetse/util/property_matcher.ts b/packages/concatjs/internal/tsetse/util/property_matcher.ts deleted file mode 100644 index 0caea62..0000000 --- a/packages/concatjs/internal/tsetse/util/property_matcher.ts +++ /dev/null
@@ -1,60 +0,0 @@ -import * as ts from 'typescript'; - -// TODO: Export the matched node kinds here. -/** - * This class matches a property access node, based on a property holder type - * (through its name), i.e. a class, and a property name. - * - * The logic is voluntarily simple: if a matcher for `a.b` tests a `x.y` node, - * it will return true if: - * - `x` is of type `a` either directly (name-based) or through inheritance - * (ditto), - * - and, textually, `y` === `b`. - * - * Note that the logic is different from TS's type system: this matcher doesn't - * have any knowledge of structural typing. - */ -export class PropertyMatcher { - static fromSpec(spec: string): PropertyMatcher { - if (spec.indexOf('.prototype.') === -1) { - throw new Error(`BANNED_PROPERTY expects a .prototype in your query.`); - } - const requestParser = /^([\w\d_.-]+)\.prototype\.([\w\d_.-]+)$/; - const matches = requestParser.exec(spec); - if (!matches) { - throw new Error('Cannot understand the BannedProperty spec' + spec); - } - const [bannedType, bannedProperty] = matches.slice(1); - return new PropertyMatcher(bannedType, bannedProperty); - } - - constructor(readonly bannedType: string, readonly bannedProperty: string) {} - - /** - * @param n The PropertyAccessExpression we're looking at. - */ - matches(n: ts.PropertyAccessExpression, tc: ts.TypeChecker) { - return n.name.text === this.bannedProperty && - this.typeMatches(tc.getTypeAtLocation(n.expression)); - } - - private exactTypeMatches(inspectedType: ts.Type): boolean { - const typeSymbol = inspectedType.getSymbol() || false; - return typeSymbol && typeSymbol.getName() === this.bannedType; - } - - // TODO: Account for unknown types/ '?', and 'loose type matches', i.e. if the - // actual type is a supertype of the prohibited type. - typeMatches(inspectedType: ts.Type): boolean { - if (this.exactTypeMatches(inspectedType)) { - return true; - } - // If the type is an intersection/union, check if any of the component matches - if (inspectedType.isUnionOrIntersection()) { - return inspectedType.types.some(comp => this.typeMatches(comp)); - } - - const baseTypes = inspectedType.getBaseTypes() || []; - return baseTypes.some(base => this.typeMatches(base)); - } -}
diff --git a/packages/concatjs/internal/tsetse/util/property_matcher_test.ts b/packages/concatjs/internal/tsetse/util/property_matcher_test.ts deleted file mode 100644 index 74fded8..0000000 --- a/packages/concatjs/internal/tsetse/util/property_matcher_test.ts +++ /dev/null
@@ -1,137 +0,0 @@ -import 'jasmine'; -import {ConformancePatternRule, ErrorCode, PatternKind} from '../rules/conformance_pattern_rule'; -import {compileAndCheck, customMatchers} from './testing/test_support'; - -describe('PropertyMatcher', () => { - beforeEach(() => { - jasmine.addMatchers(customMatchers); - }); - - it('matches simple property access on dom properties', () => { - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'No Location#href access', - kind: PatternKind.BANNED_PROPERTY, - values: ['Location.prototype.href'], - }; - const source = 'const href = location.href;'; - const results = compileAndCheck(new ConformancePatternRule(config), source); - - expect(results).toHaveFailuresMatching({ - matchedCode: 'location.href', - messageText: 'No Location#href access', - }); - }); - - it('matches simple property access on properties of TS built-in types', - () => { - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'No Array#map access', - kind: PatternKind.BANNED_PROPERTY, - values: ['Array.prototype.map'], - }; - const source = '[].map(() => 1);'; - - const results = - compileAndCheck(new ConformancePatternRule(config), source); - - expect(results).toHaveFailuresMatching({ - matchedCode: '[].map', - messageText: 'No Array#map access', - }); - }); - - it('matches simple property access on properties of user-defined global types', - () => { - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'No Ty#foo access', - kind: PatternKind.BANNED_PROPERTY, - values: ['Ty.prototype.foo'], - }; - const funcPropAccess = `class Ty { - foo() {} - bar: number = 1; - } - new Ty().foo(); - `; - let results = - compileAndCheck(new ConformancePatternRule(config), funcPropAccess); - - expect(results).toHaveFailuresMatching({ - matchedCode: 'new Ty().foo', - messageText: 'No Ty#foo access', - }); - - const nonFuncPropAccess = `class Ty { - foo: number = 1; - bar() {} - } - new Ty().foo; - `; - results = compileAndCheck( - new ConformancePatternRule(config), nonFuncPropAccess); - - expect(results).toHaveFailuresMatching({ - matchedCode: 'new Ty().foo', - messageText: 'No Ty#foo access', - }); - }); - - it('matches property on indirect base types', () => { - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'No Element#innerHTML access', - kind: PatternKind.BANNED_PROPERTY_WRITE, - values: ['Element.prototype.innerHTML'], - }; - // Element is an indirect base of HTMLDivElement (HTMLDivElement extends - // HTMLElement, which extends Element). - const proprAcessOnIndirectBaseType = - `declare var div: HTMLDivElement; div.innerHTML = 'foo'`; - - const results = compileAndCheck( - new ConformancePatternRule(config), proprAcessOnIndirectBaseType); - - expect(results).toHaveFailuresMatching({ - matchedCode: `div.innerHTML = 'foo'`, - messageText: 'No Element#innerHTML access', - }); - }); - - it('does not match in-module defined type', () => { - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'No Location#replace access', - kind: PatternKind.BANNED_PROPERTY, - values: ['Location.prototype.location'], - }; - const source = `export {}; // export makes the file a module - class Location { replace(x: string) {} } - new Location().replace('a');`; - const results = compileAndCheck(new ConformancePatternRule(config), source); - - expect(results).toHaveNoFailures(); - }); - - it('does not match properties after fancy type casts', () => { - const config = { - errorCode: ErrorCode.CONFORMANCE_PATTERN, - errorMessage: 'No Location#href access', - kind: PatternKind.BANNED_PROPERTY, - values: ['Location.prototype.replace'], - }; - const source = [ - // Grey area of tests that don't trigger, but probably could - '(location as unknown as {replace: (url: string) => void}).replace(\'\');', - // We don't trigger on any, since it's not the right type. - 'const locationAsAny: any = location;', - 'locationAsAny.replace(\'https://example.com/script.js\');', - ]; - const results = - compileAndCheck(new ConformancePatternRule(config), ...source); - - expect(results).toHaveNoFailures(); - }); -});
diff --git a/packages/concatjs/internal/tsetse/util/testing/test_support.ts b/packages/concatjs/internal/tsetse/util/testing/test_support.ts deleted file mode 100644 index a3c32d3..0000000 --- a/packages/concatjs/internal/tsetse/util/testing/test_support.ts +++ /dev/null
@@ -1,302 +0,0 @@ -import 'jasmine'; - -import * as crypto from 'crypto'; -import * as fs from 'fs'; -import * as os from 'os'; -import * as path from 'path'; -import * as ts from 'typescript'; - -import {Checker} from '../../checker'; -import {Failure, fixToString} from '../../failure'; -import {AbstractRule} from '../../rule'; - - - -/** - * Turns the provided source (as strings) into a ts.Program. The source files - * will be named `.../file_${n}.ts`, with n the index of the source file in - * the `sourceCode` array. - */ -export function compile(...sourceCode: string[]): ts.Program { - const temporaryFolder = os.tmpdir() + path.sep + - `tslint_test_input_${crypto.randomBytes(16).toString('hex')}`; - const fullPaths: string[] = []; - sourceCode.forEach((s, i) => { - fullPaths.push(`${temporaryFolder}${path.sep}file_${i}.ts`); - }); - - let error: Error|undefined = undefined; - let program: ts.Program|undefined = undefined; - try { // Wrap it all in a try/finally to clean up the temp files afterwards - fs.mkdirSync(temporaryFolder); - sourceCode.forEach((s, i) => { - fs.writeFileSync(fullPaths[i], s); - }); - program = ts.createProgram(fullPaths, {}); - if (ts.getPreEmitDiagnostics(program).length !== 0) { - throw new Error( - 'Your program does not compile cleanly. Diagnostics:\n' + - ts.formatDiagnostics( - ts.getPreEmitDiagnostics(program), ts.createCompilerHost({}))); - } - } catch (e) { - error = e; - } finally { - fullPaths.forEach(p => fs.unlinkSync(p)); - fs.rmdirSync(temporaryFolder); - } - if (program && !error) { - return program; - } else { - throw error; - } -} - -function check(rule: AbstractRule, program: ts.Program): Failure[] { - const checker = new Checker(program); - rule.register(checker); - return program.getSourceFiles() - .map(s => checker.execute(s)) - .reduce((prev, cur) => prev.concat(cur)); -} - -/** Builds and run the given Rule upon the source files that were provided. */ -export function compileAndCheck( - rule: AbstractRule, ...sourceCode: string[]): Failure[] { - const program = compile(...sourceCode); - return check(rule, program); -} - -/** Turns a Failure to a fileName. */ -export function toFileName(f: Failure) { - const file = f.toDiagnostic().file; - return file ? file.fileName : 'unknown'; -} - -/** - * Returns the location the temp directory for that platform (with forward - * slashes). - */ -export function getTempDirForAllowlist() { - // TS uses forward slashes on Windows ¯\_(ツ)_/¯ - return os.platform() === 'win32' ? os.tmpdir() : os.tmpdir(); -} - -interface FailureExpectations { - fileName?: string; - start?: number; - end?: number; - matchedCode?: string; - messageText?: string; - fix?: FixExpectations; -} - -type FixExpectations = Array< - {fileName?: string; start?: number; end?: number; replacement?: string;}>; - - -function failureMatchesExpectation( - f1: Failure, exp: FailureExpectations): {pass: boolean, message: string} { - const diagnostic = f1.toDiagnostic(); - let regrets = ''; - if (exp === undefined) { - regrets += 'The matcher requires two arguments. '; - } - if (exp.fileName) { - if (!diagnostic.file) { - regrets += `Expected diagnostic to have a source file, but it had ${ - diagnostic.file}. `; - } else if (!diagnostic.file.fileName.endsWith(exp.fileName)) { - regrets += - `Expected ${diagnostic.file.fileName} to end with ${exp.fileName}. `; - } - } - if (exp.messageText !== undefined && - exp.messageText !== diagnostic.messageText) { - regrets += - expectation('errorMessage', exp.messageText, diagnostic.messageText); - } - if (exp.start !== undefined && diagnostic.start !== exp.start) { - regrets += expectation('start', exp.start, diagnostic.start); - } - if (exp.end !== undefined && diagnostic.end !== exp.end) { - regrets += expectation('end', exp.end, diagnostic.end); - } - if (exp.matchedCode) { - if (!diagnostic.file) { - regrets += `Expected diagnostic to have a source file, but it had ${ - diagnostic.file}. `; - } else if (diagnostic.start === undefined) { - // I don't know how this could happen, but typings say so. - regrets += `Expected diagnostic to have a starting position. `; - } else { - const foundMatchedCode = diagnostic.file.getFullText().slice( - Number(diagnostic.start), diagnostic.end); - if (foundMatchedCode !== exp.matchedCode) { - regrets += `Expected diagnostic to match ${exp.matchedCode}, but was ${ - foundMatchedCode} (from ${Number(diagnostic.start)} to ${ - diagnostic.end}). `; - } - } - } - if (exp.fix) { - const {pass, message: fixMessage} = fixMatchesExpectation(f1, exp.fix); - if (!pass) { - regrets += fixMessage; - } - } - return {pass: regrets === '', message: regrets}; -} - -function fixMatchesExpectation(failure: Failure, expected: FixExpectations): - {pass: boolean, message: string} { - let regrets = ''; - const actualFix = failure.toDiagnostic().fix; - if (!actualFix) { - regrets += `Expected ${failure.toString()} to have fix ${ - JSON.stringify(expected)}. `; - } else if (actualFix.changes.length !== expected.length) { - regrets += `Expected ${expected.length} individual changes, got ${ - actualFix.changes.length}. `; - if (actualFix.changes.length) { - regrets += '\n' + fixToString(actualFix); - } - } else { - for (let i = 0; i < expected.length; i++) { - const e = expected[i]; - const a = actualFix.changes[i]; - if (e.start !== undefined && e.start !== a.start) { - regrets += - expectation(`${i}th individualChange's start`, e.start, a.start); - } - if (e.end !== undefined && e.end !== a.end) { - regrets += expectation(`${i}th individualChange's end`, e.end, a.end); - } - if (e.replacement !== undefined && e.replacement !== a.replacement) { - regrets += expectation( - `${i}th individualChange's replacement`, e.replacement, - a.replacement); - } - if (e.fileName !== undefined && e.fileName !== a.sourceFile.fileName) { - regrets += expectation( - `${i}th individualChange's fileName`, e.fileName, - a.sourceFile.fileName); - } - } - } - - return {pass: regrets === '', message: regrets}; -} - - -/** - * Custom matcher for Jasmine, for a better experience matching failures and - * fixes. - */ -export const customMatchers: jasmine.CustomMatcherFactories = { - - toBeFailureMatching(): jasmine.CustomMatcher { - return {compare: failureMatchesExpectation}; - }, - - /** Checks that a Failure has the expected Fix field. */ - toHaveFixMatching(): jasmine.CustomMatcher { - return {compare: fixMatchesExpectation}; - }, - - toHaveNFailures(): jasmine.CustomMatcher { - return { - compare: (actual: Failure[], expected: number) => { - if (actual.length === expected) { - return {pass: true}; - } else { - let message = - `Expected ${expected} Failures, but found ${actual.length}.`; - if (actual.length) { - message += '\n' + actual.map(f => f.toString()).join('\n'); - } - return {pass: false, message}; - } - } - }; - }, - - toHaveFailuresMatching(): jasmine.CustomMatcher { - return { - compare: (actual: Failure[], ...expected: FailureExpectations[]) => { - if (actual.length !== expected.length) { - let message = - `Expected ${expected} Failures, but found ${actual.length}.`; - if (actual.length) { - message += '\n' + actual.map(f => f.toString()).join('\n'); - } - return {pass: false, message}; - } - let pass = true, message = ''; - for (let i = 0; i < actual.length; i++) { - const comparison = failureMatchesExpectation(actual[i], expected[i]); - pass = pass && comparison.pass; - if (comparison.message) { - message += `For failure ${i}: ${comparison.message}\n`; - } - message += comparison.message; - } - return {pass, message}; - } - }; - }, - - /** - * Asserts that a Failure has no fix. - */ - toHaveNoFix(): jasmine.CustomMatcher { - return { - compare: (actualFailure: Failure) => { - return { - pass: actualFailure.toDiagnostic().fix === undefined, - message: 'This failure should not have a fix.' - }; - } - }; - }, - - toHaveNoFailures(): jasmine.CustomMatcher { - return { - compare: (actual: Failure[]) => { - if (actual.length !== 0) { - let message = `Expected no Failures, but found ${actual.length}.`; - message += '\n' + actual.map(f => f.toString()).join('\n'); - return {pass: false, message}; - } - return {pass: true, message: ''}; - } - }; - } -}; - -function expectation<T>(fieldname: string, expectation: T, actual: T) { - return `Expected .${fieldname} to be ${expectation}, was ${actual}. `; -} - -// And the matching type -declare global { - namespace jasmine { - interface Matchers<T> { - toBeFailureMatching(expected: FailureExpectations): void; - - - /** Checks that a Failure has the expected Fix field. */ - toHaveFixMatching(expected: FixExpectations): void; - - /** Asserts that a Failure has no fix. */ - toHaveNoFix(): void; - - toHaveNFailures(expected: number): void; - - toHaveFailuresMatching(...expected: FailureExpectations[]): void; - - /** Asserts that the results are empty. */ - toHaveNoFailures(): void; - } - } -}
diff --git a/packages/concatjs/package.bzl b/packages/concatjs/package.bzl deleted file mode 100644 index b007879..0000000 --- a/packages/concatjs/package.bzl +++ /dev/null
@@ -1,75 +0,0 @@ -# Copyright 2018 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Package file which defines build dependencies -""" - -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") -load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") - -def rules_typescript_dev_dependencies(): - """ - Fetch dependencies needed for local development. - - These are in this file to keep version information in one place, and make the WORKSPACE - shorter. - - Also this allows other repos to reference our sources with local_repository and install the needed deps. - """ - - maybe( - http_archive, - name = "build_bazel_rules_nodejs", - sha256 = "b6670f9f43faa66e3009488bbd909bc7bc46a5a9661a33f6bc578068d1837f37", - urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.3.0/rules_nodejs-1.3.0.tar.gz"], - ) - - # For protocol buffers - maybe( - http_archive, - name = "io_bazel", - urls = ["https://github.com/bazelbuild/bazel/releases/download/5.3.1/bazel-5.3.1-dist.zip"], - sha256 = "18486e7152ca26b26585e9b2a6f49f332b116310d3b7e5b70583f1f1f24bb8ae", - ) - - # For building concatjs_devserver binary - # See https://github.com/bazelbuild/rules_go#setup for the latest version. - maybe( - http_archive, - name = "io_bazel_rules_go", - sha256 = "8e968b5fcea1d2d64071872b12737bbb5514524ee5f0a4f54f5920266c261acb", - urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.28.0/rules_go-v0.28.0.zip", - "https://github.com/bazelbuild/rules_go/releases/download/v0.28.0/rules_go-v0.28.0.zip", - ], - ) - - # go_repository is defined in bazel_gazelle - maybe( - http_archive, - name = "bazel_gazelle", - sha256 = "62ca106be173579c0a167deb23358fdfe71ffa1e4cfdddf5582af26520f1c66f", - urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.23.0/bazel-gazelle-v0.23.0.tar.gz", - "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.23.0/bazel-gazelle-v0.23.0.tar.gz", - ], - ) - - maybe( - http_archive, - name = "com_google_protobuf", - sha256 = "98e615d592d237f94db8bf033fba78cd404d979b0b70351a9e5aaff725398357", - strip_prefix = "protobuf-3.9.1", - urls = ["https://github.com/protocolbuffers/protobuf/archive/v3.9.1.tar.gz"], - )
diff --git a/packages/concatjs/package.json b/packages/concatjs/package.json deleted file mode 100644 index a60da18..0000000 --- a/packages/concatjs/package.json +++ /dev/null
@@ -1,41 +0,0 @@ -{ - "name": "@bazel/concatjs", - "description": "Fast JavaScript bundler for Bazel", - "license": "Apache-2.0", - "version": "0.0.0-PLACEHOLDER", - "repository": { - "type": "git", - "url": "https://github.com/bazelbuild/rules_nodejs.git", - "directory": "packages/concatjs" - }, - "bugs": { - "url": "https://github.com/bazelbuild/rules_nodejs/issues" - }, - "keywords": [ - "bazel" - ], - "main": "./index.js", - "typings": "./index.d.ts", - "//main": "./internal/tsc_wrapped/index.js", - "//typings": "./internal/tsc_wrapped/index.d.ts", - "bin": { - "tsc_wrapped": "./internal/tsc_wrapped/tsc_wrapped.js" - }, - "dependencies": { - "protobufjs": "6.8.8", - "source-map-support": "0.5.9", - "tsutils": "3.21.0" - }, - "peerDependencies": { - "karma": ">=4.0.0", - "karma-chrome-launcher": ">=2.0.0", - "karma-firefox-launcher": ">=1.0.0", - "karma-jasmine": ">=2.0.0", - "karma-junit-reporter": ">=2.0.0", - "karma-requirejs": ">=1.0.0", - "karma-sourcemap-loader": ">=0.3.0" - }, - "scripts": { - "postinstall": "node npm_version_check.js" - } -}
diff --git a/packages/concatjs/replacements.bzl b/packages/concatjs/replacements.bzl deleted file mode 100644 index 97af89d..0000000 --- a/packages/concatjs/replacements.bzl +++ /dev/null
@@ -1,34 +0,0 @@ -# Copyright 2019 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Replacements for @bazel/typescript package -""" - -load("@build_bazel_rules_nodejs//:index.bzl", "COMMON_REPLACEMENTS") - -TYPESCRIPT_REPLACEMENTS = dict( - COMMON_REPLACEMENTS, - **{ - "#@external ": "", - "//packages/concatjs/web_test:karma_bin": "@npm//karma/bin:karma", - # This BEGIN-DEV fencing is required as files pulled in from - # //packages/concatjs:npm_bazel_typescript_package - # use this alternate fencing - "(#|\\/\\/)\\s+BEGIN-DEV-ONLY[\\w\\W]+?(#|\\/\\/)\\s+END-DEV-ONLY": "", - # This file gets vendored into our repo - "//packages/concatjs/internal:common": "//@bazel/concatjs/internal:common", - # Replace the local compiler label with one that comes from npm - "//packages/concatjs/internal:tsc_wrapped_bin": "//@bazel/concatjs/bin:tsc_wrapped", - } -)
diff --git a/packages/concatjs/test/absolute_imports/BUILD.bazel b/packages/concatjs/test/absolute_imports/BUILD.bazel deleted file mode 100644 index 3de7d9c..0000000 --- a/packages/concatjs/test/absolute_imports/BUILD.bazel +++ /dev/null
@@ -1,23 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -load("//packages/concatjs:index.bzl", "ts_library") - -ts_library( - name = "absolute_imports", - srcs = glob(["*.ts"]), - expected_diagnostics = [ - "TS2307: Cannot find module 'packages/concatjs/test/absolute_imports/foo'", - ], -)
diff --git a/packages/concatjs/test/absolute_imports/foo.ts b/packages/concatjs/test/absolute_imports/foo.ts deleted file mode 100644 index ea26727..0000000 --- a/packages/concatjs/test/absolute_imports/foo.ts +++ /dev/null
@@ -1,2 +0,0 @@ -const a = 1; -export default a;
diff --git a/packages/concatjs/test/absolute_imports/importer.ts b/packages/concatjs/test/absolute_imports/importer.ts deleted file mode 100644 index f0225f6..0000000 --- a/packages/concatjs/test/absolute_imports/importer.ts +++ /dev/null
@@ -1 +0,0 @@ -import foo from 'packages/concatjs/test/absolute_imports/foo';
diff --git a/packages/concatjs/test/angular_plugin/BUILD.bazel b/packages/concatjs/test/angular_plugin/BUILD.bazel deleted file mode 100644 index 1364da8..0000000 --- a/packages/concatjs/test/angular_plugin/BUILD.bazel +++ /dev/null
@@ -1,23 +0,0 @@ -load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary") - -# Custom ts_library compiler that runs tsc_wrapped with angular/compiler-cli statically linked -# This can be used with worker mode because we don't need the linker at runtime to make -# the angular plugin loadable -# Just a clone of //packages/concatjs/internal:tsc_wrapped with added deps -nodejs_binary( - name = "tsc_wrapped_with_angular", - data = [ - "//packages/concatjs/internal:tsc_wrapped", - "//packages/concatjs/third_party/github.com/bazelbuild/bazel/src/main/protobuf:worker_protocol.proto", - "@npm//@angular/compiler-cli", - "@npm//protobufjs", - "@npm//source-map-support", - "@npm//tsickle", - "@npm//tsutils", - "@npm//typescript", - ], - entry_point = "//packages/concatjs/internal:tsc_wrapped/tsc_wrapped.js", - # TODO: turn on --worker_sandboxing and remove this flag to see failure to load the plugin - templated_args = ["--bazel_patch_module_resolver"], - visibility = ["//packages/concatjs/test/angular_plugin:__subpackages__"], -)
diff --git a/packages/concatjs/test/angular_plugin/compiled_output/BUILD.bazel b/packages/concatjs/test/angular_plugin/compiled_output/BUILD.bazel deleted file mode 100644 index 2a17442..0000000 --- a/packages/concatjs/test/angular_plugin/compiled_output/BUILD.bazel +++ /dev/null
@@ -1,36 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -load("//packages/jasmine:index.bzl", "jasmine_node_test") -load("//packages/concatjs:index.bzl", "ts_library") - -ts_library( - name = "compiled_output", - srcs = glob(["*.ts"]), - angular_assets = [ - "comp.ng.html", - ], - compiler = "//packages/concatjs/test/angular_plugin:tsc_wrapped_with_angular", - tsconfig = ":tsconfig.json", - use_angular_plugin = True, - deps = [ - "@npm//@angular/core", - ], -) - -jasmine_node_test( - name = "test", - srcs = ["spec.js"], - data = ["compiled_output"], -)
diff --git a/packages/concatjs/test/angular_plugin/compiled_output/comp.ng.html b/packages/concatjs/test/angular_plugin/compiled_output/comp.ng.html deleted file mode 100644 index c38b0fe..0000000 --- a/packages/concatjs/test/angular_plugin/compiled_output/comp.ng.html +++ /dev/null
@@ -1 +0,0 @@ -<div>{{thing}}</div>
diff --git a/packages/concatjs/test/angular_plugin/compiled_output/component.ts b/packages/concatjs/test/angular_plugin/compiled_output/component.ts deleted file mode 100644 index d4f2f68..0000000 --- a/packages/concatjs/test/angular_plugin/compiled_output/component.ts +++ /dev/null
@@ -1,6 +0,0 @@ -import {Component} from '@angular/core'; - -@Component({ - templateUrl: './comp.ng.html', -}) -export class Comp { thing: string; }
diff --git a/packages/concatjs/test/angular_plugin/compiled_output/spec.js b/packages/concatjs/test/angular_plugin/compiled_output/spec.js deleted file mode 100644 index 22ceb00..0000000 --- a/packages/concatjs/test/angular_plugin/compiled_output/spec.js +++ /dev/null
@@ -1,13 +0,0 @@ -const runfiles = require(process.env['BAZEL_NODE_RUNFILES_HELPER']); -const fs = require('fs'); - -describe('angular plugin', () => { - it('should produce typings with Angular metadata', () => { - expect(fs.readFileSync(runfiles.resolvePackageRelative('component.d.ts'), 'utf-8')) - .toContain('ɵɵComponentDeclaration<Comp'); - }); - it('should produce JS code with Angular runtime', () => { - expect(fs.readFileSync(runfiles.resolvePackageRelative('component.js'), 'utf-8')) - .toContain('defineComponent({ type: Comp'); - }); -});
diff --git a/packages/concatjs/test/angular_plugin/compiled_output/tsconfig.json b/packages/concatjs/test/angular_plugin/compiled_output/tsconfig.json deleted file mode 100644 index 90fbfaf..0000000 --- a/packages/concatjs/test/angular_plugin/compiled_output/tsconfig.json +++ /dev/null
@@ -1,5 +0,0 @@ -{ - "compilerOptions": { - "experimentalDecorators": true - } -} \ No newline at end of file
diff --git a/packages/concatjs/test/angular_plugin/diagnostic/BUILD.bazel b/packages/concatjs/test/angular_plugin/diagnostic/BUILD.bazel deleted file mode 100644 index d620ba7..0000000 --- a/packages/concatjs/test/angular_plugin/diagnostic/BUILD.bazel +++ /dev/null
@@ -1,32 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -load("//packages/concatjs:index.bzl", "ts_library") - -ts_library( - name = "angular_plugin", - srcs = glob(["*.ts"]), - angular_assets = [ - "comp.ng.html", - ], - compiler = "//packages/concatjs/test/angular_plugin:tsc_wrapped_with_angular", - expected_diagnostics = [ - "TS2339: \\[ngtsc\\] Property 'thing' does not exist on type 'Comp'", - ], - tsconfig = ":tsconfig.json", - use_angular_plugin = True, - deps = [ - "@npm//@angular/core", - ], -)
diff --git a/packages/concatjs/test/angular_plugin/diagnostic/comp.ng.html b/packages/concatjs/test/angular_plugin/diagnostic/comp.ng.html deleted file mode 100644 index c38b0fe..0000000 --- a/packages/concatjs/test/angular_plugin/diagnostic/comp.ng.html +++ /dev/null
@@ -1 +0,0 @@ -<div>{{thing}}</div>
diff --git a/packages/concatjs/test/angular_plugin/diagnostic/component.ts b/packages/concatjs/test/angular_plugin/diagnostic/component.ts deleted file mode 100644 index fe2ba1c..0000000 --- a/packages/concatjs/test/angular_plugin/diagnostic/component.ts +++ /dev/null
@@ -1,6 +0,0 @@ -import {Component} from '@angular/core'; - -@Component({ - templateUrl: './comp.ng.html', -}) -export class Comp {}
diff --git a/packages/concatjs/test/angular_plugin/diagnostic/tsconfig.json b/packages/concatjs/test/angular_plugin/diagnostic/tsconfig.json deleted file mode 100644 index 90fbfaf..0000000 --- a/packages/concatjs/test/angular_plugin/diagnostic/tsconfig.json +++ /dev/null
@@ -1,5 +0,0 @@ -{ - "compilerOptions": { - "experimentalDecorators": true - } -} \ No newline at end of file
diff --git a/packages/concatjs/test/devmode_consumer/BUILD.bazel b/packages/concatjs/test/devmode_consumer/BUILD.bazel deleted file mode 100644 index c605fc8..0000000 --- a/packages/concatjs/test/devmode_consumer/BUILD.bazel +++ /dev/null
@@ -1,15 +0,0 @@ -load(":devmode_consumer.bzl", "devmode_consumer") - -devmode_consumer( - name = "devmode_consumer", - deps = ["//packages/concatjs/test/foobar:bar_ts_library"], -) - -sh_test( - name = "devmode_consumer_test", - srcs = ["devmode_consumer_test.sh"], - data = [ - ":devmode_consumer", - "@build_bazel_rules_nodejs//third_party/github.com/bazelbuild/bazel/tools/bash/runfiles", - ], -)
diff --git a/packages/concatjs/test/devmode_consumer/devmode_consumer.bzl b/packages/concatjs/test/devmode_consumer/devmode_consumer.bzl deleted file mode 100644 index ed90e10..0000000 --- a/packages/concatjs/test/devmode_consumer/devmode_consumer.bzl +++ /dev/null
@@ -1,37 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Example of a rule that requires es2015 (devmode) inputs. -""" - -load("@build_bazel_rules_nodejs//:providers.bzl", "JSNamedModuleInfo") - -def _devmode_consumer(ctx): - sources_depsets = [] - for dep in ctx.attr.deps: - if JSNamedModuleInfo in dep: - sources_depsets.append(dep[JSNamedModuleInfo].sources) - sources = depset(transitive = sources_depsets) - - return [DefaultInfo( - files = sources, - runfiles = ctx.runfiles(transitive_files = sources), - )] - -devmode_consumer = rule( - implementation = _devmode_consumer, - attrs = { - "deps": attr.label_list(), - }, -)
diff --git a/packages/concatjs/test/devmode_consumer/devmode_consumer_test.sh b/packages/concatjs/test/devmode_consumer/devmode_consumer_test.sh deleted file mode 100755 index d8ee7d3..0000000 --- a/packages/concatjs/test/devmode_consumer/devmode_consumer_test.sh +++ /dev/null
@@ -1,72 +0,0 @@ -#!/bin/bash - -# --- begin runfiles.bash initialization v2 --- -# Copy-pasted from the Bazel Bash runfiles library v2. -set -uo pipefail; f=build_bazel_rules_nodejs/third_party/github.com/bazelbuild/bazel/tools/bash/runfiles/runfiles.bash -source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \ - source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \ - source "$0.runfiles/$f" 2>/dev/null || \ - source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ - source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ - { echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e -# --- end runfiles.bash initialization v2 --- - -readonly LIBRARY_JS=$(cat $(rlocation "build_bazel_rules_nodejs/packages/concatjs/test/some_library/library.js")) -readonly BAR_JS=$(cat $(rlocation "build_bazel_rules_nodejs/packages/concatjs/test/foobar/bar.js")) -readonly FOO_JS=$(cat $(rlocation "build_bazel_rules_nodejs/packages/concatjs/test/foobar/foo.js")) - -# should produce named UMD modules -if [[ "$LIBRARY_JS" != *"define(\"some-lib\""* ]]; then - echo "Expected library.js to declare module named some-lib, but was" - echo "$LIBRARY_JS" - exit 1 -fi - -# should produce named UMD modules -if [[ "$BAR_JS" != *"define(\"build_bazel_rules_nodejs/packages/concatjs/test/foobar/bar\""* ]]; then - echo "Expected bar.js to declare named module, but was" - echo "$BAR_JS" - exit 1 -fi - -# should give a name to required modules -if [[ "$BAR_JS" != *"require(\"build_bazel_rules_nodejs/packages/concatjs/test/foobar/foo\")"* ]]; then - echo "Expected bar.js to require named module foo, but was" - echo "$BAR_JS" - exit 1 -fi - -# should give a name to required modules from other compilation unit -if [[ "$BAR_JS" != *"require(\"some-lib\")"* ]]; then - echo "Expected bar.js to require named module library, but was" - echo "$BAR_JS" - exit 1 -fi - -# should give a name to required generated modules without bazel-bin -if [[ "$BAR_JS" != *"require(\"build_bazel_rules_nodejs/packages/concatjs/test/generated_ts/foo\")"* ]]; then - echo "Expected bar.js to require generated named module foo, but was" - echo "$BAR_JS" - exit 1 -fi - -# should not give a module name to external modules -if [[ "$BAR_JS" != *"require(\"typescript\")"* ]]; then - echo "Expected bar.js to require typescript by its original name, but was" - echo "$BAR_JS" - exit 1 -fi - -# should produce named UMD modules -if [[ "$FOO_JS" != *"define(\"build_bazel_rules_nodejs/packages/concatjs/test/foobar/foo\""* ]]; then - echo "Expected foo.js to declare named module, but was" - echo "$FOO_JS" - exit 1 -fi - -# should produce es2015 classes -if [[ "$FOO_JS" != *"class Greeter"* ]]; then - echo "Expected foo.js produce a es2015, but was" - echo "$FOO_JS" - exit 1 -fi \ No newline at end of file
diff --git a/packages/concatjs/test/errorchecks/BUILD.bazel b/packages/concatjs/test/errorchecks/BUILD.bazel deleted file mode 100644 index 66876ce..0000000 --- a/packages/concatjs/test/errorchecks/BUILD.bazel +++ /dev/null
@@ -1,24 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -load("//packages/concatjs:index.bzl", "ts_library") - -ts_library( - name = "erroneous", - srcs = glob(["*.ts"]), - expected_diagnostics = [ - "TS2322: Type 'string' is not assignable to type 'number'", - "TS2304: Cannot find name 'TypeThatDoesNotExist'", - ], -)
diff --git a/packages/concatjs/test/errorchecks/erroneous.ts b/packages/concatjs/test/errorchecks/erroneous.ts deleted file mode 100644 index b0a583c..0000000 --- a/packages/concatjs/test/errorchecks/erroneous.ts +++ /dev/null
@@ -1 +0,0 @@ -export const x: number = 'not a number';
diff --git a/packages/concatjs/test/errorchecks/erroneous_decl.d.ts b/packages/concatjs/test/errorchecks/erroneous_decl.d.ts deleted file mode 100644 index c1a3b97..0000000 --- a/packages/concatjs/test/errorchecks/erroneous_decl.d.ts +++ /dev/null
@@ -1,2 +0,0 @@ - -export declare class Foo { field: TypeThatDoesNotExist; }
diff --git a/packages/concatjs/test/es6_consumer/BUILD.bazel b/packages/concatjs/test/es6_consumer/BUILD.bazel deleted file mode 100644 index 0308c83..0000000 --- a/packages/concatjs/test/es6_consumer/BUILD.bazel +++ /dev/null
@@ -1,29 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -load(":es6_consumer.bzl", "es6_consumer") - -es6_consumer( - name = "es6_output", - deps = ["//packages/concatjs/test/foobar:bar_ts_library"], -) - -sh_test( - name = "es6_output_test", - srcs = ["es6_output_test.sh"], - data = [ - ":es6_output", - "@build_bazel_rules_nodejs//third_party/github.com/bazelbuild/bazel/tools/bash/runfiles", - ], -)
diff --git a/packages/concatjs/test/es6_consumer/es6_consumer.bzl b/packages/concatjs/test/es6_consumer/es6_consumer.bzl deleted file mode 100644 index c88a748..0000000 --- a/packages/concatjs/test/es6_consumer/es6_consumer.bzl +++ /dev/null
@@ -1,37 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Example of a rule that requires ES6 inputs. -""" - -load("@build_bazel_rules_nodejs//:providers.bzl", "JSEcmaScriptModuleInfo") - -def _es6_consumer(ctx): - sources_depsets = [] - for dep in ctx.attr.deps: - if JSEcmaScriptModuleInfo in dep: - sources_depsets.append(dep[JSEcmaScriptModuleInfo].sources) - sources = depset(transitive = sources_depsets) - - return [DefaultInfo( - files = sources, - runfiles = ctx.runfiles(transitive_files = sources), - )] - -es6_consumer = rule( - implementation = _es6_consumer, - attrs = { - "deps": attr.label_list(), - }, -)
diff --git a/packages/concatjs/test/es6_consumer/es6_output_test.sh b/packages/concatjs/test/es6_consumer/es6_output_test.sh deleted file mode 100755 index c517aad..0000000 --- a/packages/concatjs/test/es6_consumer/es6_output_test.sh +++ /dev/null
@@ -1,37 +0,0 @@ -#!/bin/bash - -# --- begin runfiles.bash initialization v2 --- -# Copy-pasted from the Bazel Bash runfiles library v2. -set -uo pipefail; f=build_bazel_rules_nodejs/third_party/github.com/bazelbuild/bazel/tools/bash/runfiles/runfiles.bash -source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \ - source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \ - source "$0.runfiles/$f" 2>/dev/null || \ - source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ - source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ - { echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e -# --- end runfiles.bash initialization v2 --- - -readonly FOO_JS=$(cat $(rlocation "build_bazel_rules_nodejs/packages/concatjs/test/foobar/foo.mjs")) -readonly BAR_JS=$(cat $(rlocation "build_bazel_rules_nodejs/packages/concatjs/test/foobar/bar.mjs")) -readonly LIBRARY_JS=$(cat $(rlocation "build_bazel_rules_nodejs/packages/concatjs/test/some_library/library.mjs")) - -# should not down-level ES2015 syntax, eg. `class` -if [[ "$FOO_JS" != *"class Greeter"* ]]; then - echo "Expected foo.js to contain 'class Greeter' but was" - echo "$FOO_JS" - exit 1 -fi - -# should not down-level ES Modules -if [[ "$LIBRARY_JS" != *"export const cool = 1;"* ]]; then - echo "Expected library.js to contain 'export const cool = 1;' but was" - echo "$LIBRARY_JS" - exit 1 -fi - -# should not down-level dynamic import -if [[ "$BAR_JS" != *"import('./foo')"* ]]; then - echo "Expected bar.js to contain 'import('./foo')' but was" - echo "$BAR_JS" - exit 1 -fi
diff --git a/packages/concatjs/test/foobar/BUILD.bazel b/packages/concatjs/test/foobar/BUILD.bazel deleted file mode 100644 index 3594aa5..0000000 --- a/packages/concatjs/test/foobar/BUILD.bazel +++ /dev/null
@@ -1,42 +0,0 @@ -load("//packages/concatjs:index.bzl", "ts_library") - -ts_library( - name = "types", - srcs = ["types.d.ts"], -) - -ts_library( - name = "foo_ts_library", - srcs = [ - "conflict.d.ts", - "foo.ts", - ], - tsconfig = ":tsconfig-foo.json", - deps = [ - ":types", - "//packages/concatjs/test/generated_ts", - ], -) - -ts_library( - name = "bar_ts_library", - srcs = ["bar.ts"], - tsconfig = ":tsconfig-bar.json", - visibility = [ - "//packages/concatjs/test/devmode_consumer:__pkg__", - "//packages/concatjs/test/es6_consumer:__pkg__", - ], - deps = [ - ":foo_ts_library", - "//packages/concatjs/test/generated_ts", - "//packages/concatjs/test/some_library:lib", - "@npm//typescript", - # Example of using the `@npm//@types` target to depend on all - # @types packages and with the types attribute of tsconfig not - # specified. In this case, typescript will automatically discover - # all types under node_modules/@types and included them in the compile. - # See getAutomaticTypeDirectiveNames in - # https://github.com/Microsoft/TypeScript/blob/master/src/compiler/moduleNameResolver.ts. - "@npm//@types", - ], -)
diff --git a/packages/concatjs/test/foobar/bar.ts b/packages/concatjs/test/foobar/bar.ts deleted file mode 100644 index a15665d..0000000 --- a/packages/concatjs/test/foobar/bar.ts +++ /dev/null
@@ -1,35 +0,0 @@ -/** - * @license - * Copyright 2017 The Bazel Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import {Greeter} from 'build_bazel_rules_nodejs/packages/concatjs/test/foobar/foo'; -import {a} from 'build_bazel_rules_nodejs/packages/concatjs/test/generated_ts/foo'; -// Repro for #31, should automatically discover @types/node -import * as fs from 'fs'; -import {cool} from 'some-lib'; -// Verify that typescript/lib/typescript.d.ts and typescript/lib/tsserverlibrary.d.ts -// are available since we must exclude all other typescript/lib/*.d.ts which are -// controlled by the compilerOptions.libs config. -// See https://github.com/bazelbuild/rules_nodejs/pull/875 for more details. -import * as ts from 'typescript'; -import * as tss from 'typescript/lib/tsserverlibrary'; - -import('./foo').then(({greeter}) => {console.log(Greeter, fs, cool, ts, greeter, a);}); - -const useTssType: tss.server.Project[] = []; -if (useTssType) { - console.log('foobar'); -}
diff --git a/packages/concatjs/test/foobar/conflict.d.ts b/packages/concatjs/test/foobar/conflict.d.ts deleted file mode 100644 index ac24e9a..0000000 --- a/packages/concatjs/test/foobar/conflict.d.ts +++ /dev/null
@@ -1,4 +0,0 @@ -// Test that we can declare an interface named Client -// which would conflict with typescript/lib/lib.webworker.d.ts if that -// lib was included but should work if that lib is not -declare class Client { id: string; }
diff --git a/packages/concatjs/test/foobar/foo.ts b/packages/concatjs/test/foobar/foo.ts deleted file mode 100644 index 4965e6b..0000000 --- a/packages/concatjs/test/foobar/foo.ts +++ /dev/null
@@ -1,30 +0,0 @@ -/** - * @license - * Copyright 2017 The Bazel Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import {Polite} from './types'; - -export class Greeter implements Polite { - constructor(public greeting: string) {} - greet(): Promise<string> { - return Promise.resolve('<h1>' + this.greeting + '</h1>'); - } -} - -export const greeter = new Greeter('Hello, world!'); -greeter.greet().then(msg => { - document.body.innerHTML = msg; -});
diff --git a/packages/concatjs/test/foobar/tsconfig-bar.json b/packages/concatjs/test/foobar/tsconfig-bar.json deleted file mode 100644 index 0307f47..0000000 --- a/packages/concatjs/test/foobar/tsconfig-bar.json +++ /dev/null
@@ -1,11 +0,0 @@ -{ - "compilerOptions": { - "strict": true, - "lib": ["es2015.promise", "dom", "es5"], - // Auto-discover all types in this configuration since we don't - // specify `types = []` and the ts_library rule depends on `@npm//@types`. - // Typescript will automatically discover all types under node_modules/@types - // and included them in the build. See getAutomaticTypeDirectiveNames in - // https://github.com/Microsoft/TypeScript/blob/master/src/compiler/moduleNameResolver.ts. - } -}
diff --git a/packages/concatjs/test/foobar/tsconfig-foo.json b/packages/concatjs/test/foobar/tsconfig-foo.json deleted file mode 100644 index 9c14e74..0000000 --- a/packages/concatjs/test/foobar/tsconfig-foo.json +++ /dev/null
@@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "strict": true, - "lib": ["es2015.promise", "dom", "es5"], - // Include the output directory in rootDirs so that generated .d.ts files - // can be used for type-checking in the editor, for example the car.proto - // produces a car.d.ts. - "rootDirs": [".", "../dist/bin/examples"], - // Explicitly set types settings so typescript doesn't auto-discover types. - // If all types are discovered then all types need to be included as deps - // or typescript may error out with TS2688: Cannot find type definition file for 'foo'. - "types": [] - } -} -
diff --git a/packages/concatjs/test/foobar/types.d.ts b/packages/concatjs/test/foobar/types.d.ts deleted file mode 100644 index dad5fdc..0000000 --- a/packages/concatjs/test/foobar/types.d.ts +++ /dev/null
@@ -1 +0,0 @@ -export interface Polite { greet(): Promise<string>; }
diff --git a/packages/concatjs/test/generated_ts/BUILD.bazel b/packages/concatjs/test/generated_ts/BUILD.bazel deleted file mode 100644 index 22176fe..0000000 --- a/packages/concatjs/test/generated_ts/BUILD.bazel +++ /dev/null
@@ -1,15 +0,0 @@ -load("//packages/concatjs:index.bzl", "ts_library") - -genrule( - name = "foo_ts", - outs = ["foo.ts"], - cmd = "echo 'export const a = 1;' > $@", -) - -ts_library( - name = "generated_ts", - srcs = [":foo.ts"], - visibility = [ - "//packages/concatjs/test/foobar:__pkg__", - ], -)
diff --git a/packages/concatjs/test/googmodule/BUILD.bazel b/packages/concatjs/test/googmodule/BUILD.bazel deleted file mode 100644 index 6297bd5..0000000 --- a/packages/concatjs/test/googmodule/BUILD.bazel +++ /dev/null
@@ -1,20 +0,0 @@ -load("//packages/jasmine:index.bzl", "jasmine_node_test") -load("//packages/concatjs:index.bzl", "ts_library") -load("//packages/concatjs/test/devmode_consumer:devmode_consumer.bzl", "devmode_consumer") - -ts_library( - name = "googmodule", - srcs = ["a.ts"], - tsconfig = ":tsconfig-googmodule.json", -) - -devmode_consumer( - name = "devmode_output", - deps = [":googmodule"], -) - -jasmine_node_test( - name = "googmodule_output_test", - srcs = ["googmodule_output_test.js"], - data = [":devmode_output"], -)
diff --git a/packages/concatjs/test/googmodule/a.ts b/packages/concatjs/test/googmodule/a.ts deleted file mode 100644 index 4f633f6..0000000 --- a/packages/concatjs/test/googmodule/a.ts +++ /dev/null
@@ -1 +0,0 @@ -const a: number = 1;
diff --git a/packages/concatjs/test/googmodule/googmodule_output_test.js b/packages/concatjs/test/googmodule/googmodule_output_test.js deleted file mode 100644 index 0ab4487..0000000 --- a/packages/concatjs/test/googmodule/googmodule_output_test.js +++ /dev/null
@@ -1,18 +0,0 @@ -const fs = require('fs'); -const runfiles = require(process.env['BAZEL_NODE_RUNFILES_HELPER']); - -describe('googmodule', () => { - let output; - beforeAll(() => { - output = runfiles.resolvePackageRelative('a.js'); - }); - - it('should have goog module syntax in devmode', () => { - expect(fs.readFileSync(output, { - encoding: 'utf-8' - })).toContain(`goog.module('build_bazel_rules_nodejs.packages.concatjs.test.googmodule.a')`); - }); - it('should have tsickle type annotations', () => { - expect(fs.readFileSync(output, {encoding: 'utf-8'})).toContain(`@type {number}`); - }); -}); \ No newline at end of file
diff --git a/packages/concatjs/test/googmodule/tsconfig-googmodule.json b/packages/concatjs/test/googmodule/tsconfig-googmodule.json deleted file mode 100644 index c3c478f..0000000 --- a/packages/concatjs/test/googmodule/tsconfig-googmodule.json +++ /dev/null
@@ -1,12 +0,0 @@ -{ - "compilerOptions": { - // Explicitly set types settings so typescript doesn't auto-discover types. - // If all types are discovered then all types need to be included as deps - // or typescript may error out with TS2688: Cannot find type definition file for 'foo'. - "types": [] - }, - "bazelOptions": { - "tsickle": true, - "googmodule": true - } -} \ No newline at end of file
diff --git a/packages/concatjs/test/lit_plugin/BUILD.bazel b/packages/concatjs/test/lit_plugin/BUILD.bazel deleted file mode 100644 index 80f6868..0000000 --- a/packages/concatjs/test/lit_plugin/BUILD.bazel +++ /dev/null
@@ -1,57 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary") -load("//packages/concatjs:index.bzl", "ts_library") - -# Custom ts_library compiler that runs tsc_wrapped with ts-lit-plugin statically linked -# This can be used with worker mode because we don't need the linker at runtime to make -# the plugin loadable -# Just a clone of //internal:tsc_wrapped with added deps -nodejs_binary( - name = "tsc_wrapped_with_lit_plugin", - data = [ - "//packages/concatjs/internal:tsc_wrapped", - "//packages/concatjs/third_party/github.com/bazelbuild/bazel/src/main/protobuf:worker_protocol.proto", - "@npm//protobufjs", - "@npm//source-map-support", - "@npm//ts-lit-plugin", - "@npm//tsickle", - "@npm//tsutils", - "@npm//typescript", - ], - entry_point = "//packages/concatjs/internal:tsc_wrapped/tsc_wrapped.js", - # TODO: turn on --worker_sandboxing and remove this flag to see failure to load the plugin - templated_args = ["--bazel_patch_module_resolver"], - visibility = ["//packages/concatjs/test/angular_plugin:__subpackages__"], -) - -ts_library( - name = "lit_plugin", - srcs = glob(["*.ts"]), - compiler = "tsc_wrapped_with_lit_plugin", - expected_diagnostics = [ - """TS2322: \\[lit\\] Unknown tag "unknown-element". Did you mean 'lit-element'?""", - "TS2322: \\[lit\\] Type '222' is not assignable to 'string", - "TS2322: \\[lit\\] Type '333' is not assignable to 'string", - """TS2322: \\[lit\\] Type '"aaa"' is not assignable to 'number'""", - """TS2322: \\[lit\\] Type '"ccc"' is not assignable to 'number'""", - "TS2322: \\[lit\\] Type '444' is not assignable to 'string", - "TS2322: \\[lit\\] Type '{ field: number; }' is not assignable to '{ field: string; }'", - ], - tsconfig = ":tsconfig.json", - deps = [ - "@npm//lit-element", - ], -)
diff --git a/packages/concatjs/test/lit_plugin/bad.ts b/packages/concatjs/test/lit_plugin/bad.ts deleted file mode 100644 index 9b834cd..0000000 --- a/packages/concatjs/test/lit_plugin/bad.ts +++ /dev/null
@@ -1,22 +0,0 @@ -import './elements'; -import {html} from 'lit-element'; - - -const objValue = { - field: 111, -}; -console.log(html` - <unknown-element></unknown-element> - - <div .id=${222}></div> - - <lit-element .strField=${333} .numField=${'aaa'}></lit-element> - - <lit-element numField="bbb"></lit-element> - - <vanilla-element .vanillaStr=${444}></vanilla-element> - - <declared-element - .declaredNumberProp=${'ccc'} - .declaredObjProp=${objValue}></declared-element> -`);
diff --git a/packages/concatjs/test/lit_plugin/declared_element.d.ts b/packages/concatjs/test/lit_plugin/declared_element.d.ts deleted file mode 100644 index 758b4f7..0000000 --- a/packages/concatjs/test/lit_plugin/declared_element.d.ts +++ /dev/null
@@ -1,14 +0,0 @@ - -export {}; - -/** An element that's only declared in a definition. */ -declare class DeclaredElement extends HTMLElement { - declaredNumberProp: number; - declaredObjProp: {field: string}; -} - -declare global { - interface HTMLElementTagNameMap { - 'declared-element': DeclaredElement; - } -}
diff --git a/packages/concatjs/test/lit_plugin/elements.ts b/packages/concatjs/test/lit_plugin/elements.ts deleted file mode 100644 index 8249d7c..0000000 --- a/packages/concatjs/test/lit_plugin/elements.ts +++ /dev/null
@@ -1,25 +0,0 @@ -/** - * @fileoverview Elements used in multiple build tests. - */ - -import {customElement, LitElement} from 'lit-element'; - - -@customElement('lit-element') -class LitElementElement extends LitElement { - strField!: string; - numField!: number; -} - -class VanillaElement extends HTMLElement { - vanillaStr = 'hi'; - str!: string; -} -customElements.define('vanilla-element', VanillaElement); - -declare global { - interface HTMLElementTagNameMap { - 'lit-element': LitElementElement; - 'vanilla-element': VanillaElement; - } -}
diff --git a/packages/concatjs/test/lit_plugin/tsconfig.json b/packages/concatjs/test/lit_plugin/tsconfig.json deleted file mode 100644 index 68b0f39..0000000 --- a/packages/concatjs/test/lit_plugin/tsconfig.json +++ /dev/null
@@ -1,33 +0,0 @@ -{ - "compilerOptions": { - "plugins": [ - { - "name": "ts-lit-plugin", - "strict": true, - "rules": { - "no-unknown-tag-name": "error", - "no-unclosed-tag": "error", - "no-unknown-property": "error", - "no-unintended-mixed-binding": "error", - "no-invalid-boolean-binding": "error", - "no-expressionless-property-binding": "error", - "no-noncallable-event-binding": "error", - "no-boolean-in-attribute-binding": "error", - "no-complex-attribute-binding": "error", - "no-nullable-attribute-binding": "error", - "no-incompatible-type-binding": "error", - "no-invalid-directive-binding": "error", - "no-incompatible-property-type": "error", - "no-unknown-property-converter": "error", - "no-invalid-attribute-name": "error", - "no-invalid-tag-name": "error", - "no-unknown-attribute": "off", - "no-missing-import": "off", - } - } - ] - }, - "exclude": [ - "node_modules" - ] -} \ No newline at end of file
diff --git a/packages/concatjs/test/reference_types_directive/BUILD.bazel b/packages/concatjs/test/reference_types_directive/BUILD.bazel deleted file mode 100644 index 752a40e..0000000 --- a/packages/concatjs/test/reference_types_directive/BUILD.bazel +++ /dev/null
@@ -1,15 +0,0 @@ -load("//packages/jasmine:index.bzl", "jasmine_node_test") -load("//packages/concatjs:index.bzl", "ts_library") - -ts_library( - name = "test_lib", - testonly = True, - srcs = glob(["*.spec.ts"]), - tsconfig = ":tsconfig.json", - deps = ["@npm//:node_modules"], -) - -jasmine_node_test( - name = "test", - deps = [":test_lib"], -)
diff --git a/packages/concatjs/test/reference_types_directive/reference_types.spec.ts b/packages/concatjs/test/reference_types_directive/reference_types.spec.ts deleted file mode 100644 index 8d52416..0000000 --- a/packages/concatjs/test/reference_types_directive/reference_types.spec.ts +++ /dev/null
@@ -1,21 +0,0 @@ -/// <reference types="zone.js" /> -/// <reference types="hammerjs" /> - -// The jasmine types are resolved because the tsconfig.json -// for this compilation includes "types": ["jasmine"]. -// This file will fail to compile if that mechanism of including types -// is broken - -describe('reference types directive resolution', () => { - it('should resolve zone.js types from node_modules/zone.js/dist/zone.js.d.ts', () => { - // The type of Zone should resolve above or this will fail to compile - let zone: Zone; - expect(1).toEqual(1); - }); - - it('should resolve hammerjs types from node_modules/@types/hammerjs/index.d.ts', () => { - // The type of HammerStatic should resolve above or this will fail to compile - let hammer: HammerStatic; - expect(1).toEqual(1); - }); -});
diff --git a/packages/concatjs/test/reference_types_directive/tsconfig.json b/packages/concatjs/test/reference_types_directive/tsconfig.json deleted file mode 100644 index 5b841a7..0000000 --- a/packages/concatjs/test/reference_types_directive/tsconfig.json +++ /dev/null
@@ -1,5 +0,0 @@ -{ - "compilerOptions": { - "types": ["jasmine"] - } -}
diff --git a/packages/concatjs/test/reference_types_directive/tsconfig_types.ts b/packages/concatjs/test/reference_types_directive/tsconfig_types.ts deleted file mode 100644 index da10367..0000000 --- a/packages/concatjs/test/reference_types_directive/tsconfig_types.ts +++ /dev/null
@@ -1,6 +0,0 @@ -// Cecking that `describe` is a valid symbol here to ensure -// that @types/jasmine is getting picked up -describe('', () => {}); -// Since "hammerjs" is not included in the types=[] array in -// tsconfig, this should result in a compile error: TS2304: Cannot find name 'Hammer' -console.log(typeof Hammer);
diff --git a/packages/concatjs/test/some_library/BUILD.bazel b/packages/concatjs/test/some_library/BUILD.bazel deleted file mode 100644 index 943bce4..0000000 --- a/packages/concatjs/test/some_library/BUILD.bazel +++ /dev/null
@@ -1,13 +0,0 @@ -load("//packages/concatjs:index.bzl", "ts_library") - -ts_library( - name = "lib", - srcs = ["library.ts"], - # Allow this library to be imported from `some-lib` - module_name = "some-lib", - # The imported path should be the library.d.ts file - module_root = "library", - visibility = [ - "//packages/concatjs/test/foobar:__pkg__", - ], -)
diff --git a/packages/concatjs/test/some_library/library.ts b/packages/concatjs/test/some_library/library.ts deleted file mode 100644 index 1cc6cf3..0000000 --- a/packages/concatjs/test/some_library/library.ts +++ /dev/null
@@ -1,18 +0,0 @@ -/** - * @license - * Copyright 2017 The Bazel Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export const cool = 1;
diff --git a/packages/concatjs/test/some_module/BUILD.bazel b/packages/concatjs/test/some_module/BUILD.bazel deleted file mode 100644 index 6d26bcf..0000000 --- a/packages/concatjs/test/some_module/BUILD.bazel +++ /dev/null
@@ -1,64 +0,0 @@ -load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary") -load("//packages/concatjs:index.bzl", "ts_library") -load("//packages/concatjs/test/some_module:run_node_test.bzl", "ts_write_file") - -# We compile this library with the module_name and package_name "sm" to make it possible to use -# `import {} from 'sm';` both at type-check time (we include the module_name mapping in the paths -# map in tsconfig) as well as runtime (we link the module tonode_modules/package_name at runtime). -ts_library( - name = "some_module", - package_name = "sm", - srcs = ["index.ts"], - module_name = "sm", -) - -ts_library( - name = "main", - srcs = ["main.ts"], - deps = [":some_module"], -) - -nodejs_binary( - name = "bin", - data = [ - ":main", - ":some_module", - ], - entry_point = ":main.ts", -) - -sh_test( - name = "module_load_test", - srcs = ["module_load_test.sh"], - data = [ - ":bin", - "@build_bazel_rules_nodejs//third_party/github.com/bazelbuild/bazel/tools/bash/runfiles", - ], -) - -# ts_library for compiling the tool -ts_library( - name = "writer", - srcs = ["writer.ts"], - deps = ["@npm//@types/node"], -) - -# nodejs_binary for generating an executable for the tool :writer -nodejs_binary( - name = "writer_bin", - data = [ - ":writer", - ], - entry_point = ":writer.ts", -) - -# custom rule using the executable above via run_node -# invoked via bazel build //packages/concatjs/test/some_module:test -# as --nobazel_patch_module_resolver is the default, the entry_point on the executable is resolved -# to the ts file - writer.ts -# this works for bazel run //packages/concatjs/test/some_module:writer_bin as this will patch require and resolve the -# ts to js -ts_write_file( - name = "test", - content = "hello world", -)
diff --git a/packages/concatjs/test/some_module/index.ts b/packages/concatjs/test/some_module/index.ts deleted file mode 100644 index 44f11e8..0000000 --- a/packages/concatjs/test/some_module/index.ts +++ /dev/null
@@ -1,3 +0,0 @@ -export function hello() { - console.log('hello world'); -}
diff --git a/packages/concatjs/test/some_module/main.ts b/packages/concatjs/test/some_module/main.ts deleted file mode 100644 index 669ef79..0000000 --- a/packages/concatjs/test/some_module/main.ts +++ /dev/null
@@ -1,3 +0,0 @@ -import {hello} from 'sm'; - -hello();
diff --git a/packages/concatjs/test/some_module/module_load_test.sh b/packages/concatjs/test/some_module/module_load_test.sh deleted file mode 100755 index 3e35ead..0000000 --- a/packages/concatjs/test/some_module/module_load_test.sh +++ /dev/null
@@ -1,19 +0,0 @@ -#!/bin/bash - -# --- begin runfiles.bash initialization v2 --- -# Copy-pasted from the Bazel Bash runfiles library v2. -set -uo pipefail; f=build_bazel_rules_nodejs/third_party/github.com/bazelbuild/bazel/tools/bash/runfiles/runfiles.bash -source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \ - source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \ - source "$0.runfiles/$f" 2>/dev/null || \ - source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ - source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ - { echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e -# --- end runfiles.bash initialization v2 --- - -readonly OUT=$($(rlocation "build_bazel_rules_nodejs/packages/concatjs/test/some_module/bin.sh")) - -if [ "$OUT" != "hello world" ]; then - echo "Expected output 'hello world' but was $OUT" - exit 1 -fi
diff --git a/packages/concatjs/test/some_module/run_node_test.bzl b/packages/concatjs/test/some_module/run_node_test.bzl deleted file mode 100644 index 84904c2..0000000 --- a/packages/concatjs/test/some_module/run_node_test.bzl +++ /dev/null
@@ -1,30 +0,0 @@ -"""Contrived custom rule for writing some content to a file using run_node""" - -load("//:providers.bzl", "run_node") - -def _ts_write_file_impl(ctx): - run_node( - ctx = ctx, - executable = "_writer", - inputs = [], - arguments = [ - ctx.attr.content, - ctx.outputs.out.path, - ], - outputs = [ctx.outputs.out], - ) - -ts_write_file = rule( - implementation = _ts_write_file_impl, - outputs = { - "out": "out.txt", - }, - attrs = { - "content": attr.string(), - "_writer": attr.label( - default = Label("//packages/concatjs/test/some_module:writer_bin"), - cfg = "exec", - executable = True, - ), - }, -)
diff --git a/packages/concatjs/test/some_module/writer.ts b/packages/concatjs/test/some_module/writer.ts deleted file mode 100644 index 794200b..0000000 --- a/packages/concatjs/test/some_module/writer.ts +++ /dev/null
@@ -1,6 +0,0 @@ -import {writeFileSync} from 'fs'; - -const content = process.argv[2]; -const outputPath = process.argv[3]; - -writeFileSync(outputPath, content, {encoding: 'utf8'});
diff --git a/packages/concatjs/test/strict_deps/BUILD b/packages/concatjs/test/strict_deps/BUILD deleted file mode 100644 index 94ccf8f..0000000 --- a/packages/concatjs/test/strict_deps/BUILD +++ /dev/null
@@ -1,54 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -load("//packages/concatjs:index.bzl", "ts_library") - -ts_library( - name = "grandparent", - srcs = ["grandparent.ts"], -) - -ts_library( - name = "parent", - srcs = ["parent.ts"], - # NB: We don't require the `@npm//semver` package for the typescript compile - # action as the .d.ts files live in @types/semver. If the resulting - # .js file is run downstream in a nodejs_binary rule, however, the - # `@npm//semver` dep will be required at that point. - # TODO: Is it desirable to automatically add @npm//semver as a transitive - # dep if @npm//@types/semver is a dep so that downtream nodejs_binary - # rules get this automatically? - deps = [ - ":grandparent", - "@npm//@types/semver", - ], -) - -ts_library( - name = "strict_deps", - srcs = ["child.ts"], - expected_diagnostics = [ - "TS2307:child\\.ts\\(2,.*transitive dependency.*not allowed", - ], - deps = [":parent"], -) - -ts_library( - name = "strict_deps_npm", - srcs = ["child2.ts"], - expected_diagnostics = [ - "TS2307:child2\\.ts\\(2,.*transitive dependency.*not allowed", - ], - deps = [":parent"], -)
diff --git a/packages/concatjs/test/strict_deps/child.ts b/packages/concatjs/test/strict_deps/child.ts deleted file mode 100644 index f0ba5d9..0000000 --- a/packages/concatjs/test/strict_deps/child.ts +++ /dev/null
@@ -1,4 +0,0 @@ -// The line below is a strict deps violation of a ts_library dep -import {Symbol} from './grandparent'; - -console.log(Symbol);
diff --git a/packages/concatjs/test/strict_deps/child2.ts b/packages/concatjs/test/strict_deps/child2.ts deleted file mode 100644 index b2ced6b..0000000 --- a/packages/concatjs/test/strict_deps/child2.ts +++ /dev/null
@@ -1,5 +0,0 @@ -// The line below is a strict deps violation of an @npm dep -import * as semver from 'semver'; -semver.valid('1.2.3'); - -console.log(Symbol);
diff --git a/packages/concatjs/test/strict_deps/grandparent.ts b/packages/concatjs/test/strict_deps/grandparent.ts deleted file mode 100644 index d352bd6..0000000 --- a/packages/concatjs/test/strict_deps/grandparent.ts +++ /dev/null
@@ -1 +0,0 @@ -export class Symbol {}
diff --git a/packages/concatjs/test/strict_deps/parent.ts b/packages/concatjs/test/strict_deps/parent.ts deleted file mode 100644 index cffa101..0000000 --- a/packages/concatjs/test/strict_deps/parent.ts +++ /dev/null
@@ -1,5 +0,0 @@ -import * as semver from 'semver'; -import {Symbol} from './grandparent'; -semver.valid('1.2.3'); -export const x = 1; -console.log(Symbol);
diff --git a/packages/concatjs/test/target_module_attributes/BUILD.bazel b/packages/concatjs/test/target_module_attributes/BUILD.bazel deleted file mode 100644 index dfe69c3..0000000 --- a/packages/concatjs/test/target_module_attributes/BUILD.bazel +++ /dev/null
@@ -1,33 +0,0 @@ -load("//packages/jasmine:index.bzl", "jasmine_node_test") -load("//packages/concatjs:index.bzl", "ts_library") -load("//packages/concatjs/test/devmode_consumer:devmode_consumer.bzl", "devmode_consumer") -load("//packages/concatjs/test/es6_consumer:es6_consumer.bzl", "es6_consumer") - -ts_library( - name = "override", - srcs = ["a.ts"], - devmode_module = "amd", - devmode_target = "es5", - prodmode_module = "amd", - prodmode_target = "es5", - tsconfig = ":tsconfig-override.json", -) - -devmode_consumer( - name = "override_devmode_output", - deps = [":override"], -) - -es6_consumer( - name = "override_prodmode_output", - deps = [":override"], -) - -jasmine_node_test( - name = "override_output_test", - srcs = ["override_output_test.js"], - data = [ - ":override_devmode_output", - ":override_prodmode_output", - ], -)
diff --git a/packages/concatjs/test/target_module_attributes/a.ts b/packages/concatjs/test/target_module_attributes/a.ts deleted file mode 100644 index d9ec9eb..0000000 --- a/packages/concatjs/test/target_module_attributes/a.ts +++ /dev/null
@@ -1 +0,0 @@ -export const a: any = () => 'hello world';
diff --git a/packages/concatjs/test/target_module_attributes/override_output_test.js b/packages/concatjs/test/target_module_attributes/override_output_test.js deleted file mode 100644 index 33bf134..0000000 --- a/packages/concatjs/test/target_module_attributes/override_output_test.js +++ /dev/null
@@ -1,37 +0,0 @@ -const fs = require('fs'); -const runfiles = require(process.env['BAZEL_NODE_RUNFILES_HELPER']); - -describe('googmodule', () => { - let devmodeOutput; - let prodmodeOutput; - beforeAll(() => { - devmodeOutput = runfiles.resolvePackageRelative('a.js'); - prodmodeOutput = runfiles.resolvePackageRelative('a.mjs'); - }); - - it('should have amd module syntax in devmode', () => { - expect(fs.readFileSync(devmodeOutput, {encoding: 'utf-8'})) - .toContain( - `define("build_bazel_rules_nodejs/packages/concatjs/test/target_module_attributes/a", ["require", "exports"], function (require, exports) {`); - }); - - it('should have es5 in devmode', () => { - const devoutput = fs.readFileSync(devmodeOutput, {encoding: 'utf-8'}); - - expect(devoutput).toContain(`a = function () { return 'hello world'; };`); - expect(devoutput).toContain(`exports.a = `); - }); - - it('should have amd module syntax in prodmode', () => { - expect(fs.readFileSync(prodmodeOutput, {encoding: 'utf-8'})) - .toContain( - `define("build_bazel_rules_nodejs/packages/concatjs/test/target_module_attributes/a", ["require", "exports"], function (require, exports) {`); - }); - - it('should have es5 in prodmode', () => { - const prodoutput = fs.readFileSync(prodmodeOutput, {encoding: 'utf-8'}); - - expect(prodoutput).toContain(`a = function () { return 'hello world'; };`); - expect(prodoutput).toContain(`exports.a = `); - }); -}); \ No newline at end of file
diff --git a/packages/concatjs/test/target_module_attributes/tsconfig-override.json b/packages/concatjs/test/target_module_attributes/tsconfig-override.json deleted file mode 100644 index 85ff8fb..0000000 --- a/packages/concatjs/test/target_module_attributes/tsconfig-override.json +++ /dev/null
@@ -1,8 +0,0 @@ -{ - "compilerOptions": { - // Explicitly set types settings so typescript doesn't auto-discover types. - // If all types are discovered then all types need to be included as deps - // or typescript may error out with TS2688: Cannot find type definition file for 'foo'. - "types": [] - }, -} \ No newline at end of file
diff --git a/packages/concatjs/test/ts_library_esm_with_jest/.babelrc b/packages/concatjs/test/ts_library_esm_with_jest/.babelrc deleted file mode 100644 index af2cb75..0000000 --- a/packages/concatjs/test/ts_library_esm_with_jest/.babelrc +++ /dev/null
@@ -1,12 +0,0 @@ -{ - "presets": [ - [ - "@babel/preset-env", - { - "targets": { - "node": "current", - }, - }, - ], - ] -} \ No newline at end of file
diff --git a/packages/concatjs/test/ts_library_esm_with_jest/BUILD.bazel b/packages/concatjs/test/ts_library_esm_with_jest/BUILD.bazel deleted file mode 100644 index 015b515..0000000 --- a/packages/concatjs/test/ts_library_esm_with_jest/BUILD.bazel +++ /dev/null
@@ -1,50 +0,0 @@ -load("@build_bazel_rules_nodejs//:index.bzl", "copy_to_bin") -load("//packages/concatjs:index.bzl", "ts_library") -load(":ts_jest_test.bzl", "ts_jest_test") - -ts_library( - name = "lib", - srcs = [ - "lib.tsx", - ], - # NB: hacky hidden configuration setting so that es6_sources does not include tsickle - # .externs.js outputs - runtime = "nodejs", - deps = [ - "@npm//@types/node", - ], -) - -# Shenanigans for Windows which doesn't have runfiles symlinks -# We need the jest config to be in the output tree where the specs are -copy_to_bin( - name = "jest_config", - srcs = [ - "jest.config.js", - ], -) - -# Same goes for babelrc. We can't add it to the jest_config copy_to_bin -# since must be a file that is passed to jest in the --config arg. -copy_to_bin( - name = "babel_rc", - srcs = [ - ".babelrc", - ], -) - -ts_jest_test( - name = "test", - srcs = [ - "lib.test.ts", - ], - data = [ - ":babel_rc", - ], - jest_config = ":jest_config", - deps = [ - ":lib", - "@npm//@babel/preset-env", - "@npm//babel-jest", - ], -)
diff --git a/packages/concatjs/test/ts_library_esm_with_jest/jest.config.js b/packages/concatjs/test/ts_library_esm_with_jest/jest.config.js deleted file mode 100644 index 77b5245..0000000 --- a/packages/concatjs/test/ts_library_esm_with_jest/jest.config.js +++ /dev/null
@@ -1,9 +0,0 @@ -module.exports = { - haste: { - enableSymlinks: true, - }, - testEnvironment: 'node', - transform: {'^.+\\.mjs?$': ['babel-jest', {configFile: __dirname + '/.babelrc'}]}, - testMatch: ['**/?(*.)(spec|test).?(m)js?(x)'], - moduleFileExtensions: ['js', 'mjs'], -};
diff --git a/packages/concatjs/test/ts_library_esm_with_jest/lib.test.ts b/packages/concatjs/test/ts_library_esm_with_jest/lib.test.ts deleted file mode 100644 index 0ea73f6..0000000 --- a/packages/concatjs/test/ts_library_esm_with_jest/lib.test.ts +++ /dev/null
@@ -1,7 +0,0 @@ -import {doStuff} from './lib'; - -describe('doStuff', () => { - it('should do some stuff', () => { - expect(doStuff('boom')).toContain('boom'); - }); -});
diff --git a/packages/concatjs/test/ts_library_esm_with_jest/lib.tsx b/packages/concatjs/test/ts_library_esm_with_jest/lib.tsx deleted file mode 100644 index 1ff5973..0000000 --- a/packages/concatjs/test/ts_library_esm_with_jest/lib.tsx +++ /dev/null
@@ -1,3 +0,0 @@ -export function doStuff(a: string): string { - return a -}
diff --git a/packages/concatjs/test/ts_library_esm_with_jest/ts_jest_test.bzl b/packages/concatjs/test/ts_library_esm_with_jest/ts_jest_test.bzl deleted file mode 100644 index ce0fcea..0000000 --- a/packages/concatjs/test/ts_library_esm_with_jest/ts_jest_test.bzl +++ /dev/null
@@ -1,44 +0,0 @@ -"""Simple macro around jest_test""" - -load("@npm//jest-cli:index.bzl", _jest_test = "jest_test") -load("//packages/concatjs:index.bzl", "ts_library") - -def ts_jest_test(name, srcs, jest_config, deps = [], data = [], **kwargs): - """A macro around the autogenerated jest_test rule that takes typescript sources - -Uses ts_library prodmode esm output""" - - ts_library( - name = "%s_ts" % name, - srcs = srcs, - data = data, - # Ideally we'd use @types/jest, but it causes typings conflicts - # if installed together with @types/jasmine and they both end up - # ambiently included in a TS compile - deps = deps + ["@npm//@types/jasmine"], - # NB: hacky hidden configuration setting so that es6_sources does not include tsickle - # .externs.js outputs - runtime = "nodejs", - # Prevent requesting the .d.ts output as that will also produce lib.test.js - # Without sandbox (e.g. windows) that will be loaded instead of lib.test.mjs - tags = ["manual"], - ) - native.filegroup( - name = "%s_esm" % name, - srcs = [":%s_ts" % name], - output_group = "es6_sources", - ) - - args = [ - "--no-cache", - "--no-watchman", - "--ci", - ] - args.extend(["--config", "$$(rlocation $(rootpath %s))" % jest_config]) - - _jest_test( - name = name, - data = [jest_config, ":%s_esm" % name] + deps + data, - templated_args = args, - **kwargs - )
diff --git a/packages/concatjs/test/tsconfig_extends/BUILD.bazel b/packages/concatjs/test/tsconfig_extends/BUILD.bazel deleted file mode 100644 index 7727eb8..0000000 --- a/packages/concatjs/test/tsconfig_extends/BUILD.bazel +++ /dev/null
@@ -1,40 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -load("//packages/concatjs:index.bzl", "ts_config", "ts_library") - -# Because our tsconfig.json has an extends property, we must also tell the -# ts_library to include the extended tsconfig file in compilations. -# The ts_library rule will generate its own tsconfig which extends from the -# src file. -# Also test that a ts_config can have a dep on another ts_config -ts_config( - name = "tsconfig", - src = "tsconfig.json", - deps = [":tsconfig_base"], -) - -ts_config( - name = "tsconfig_base", - src = "tsconfig-base.json", - deps = [":tsconfig-root.json"], -) - -ts_library( - name = "tsconfig_extends", - srcs = [ - "uses_promise.ts", - ], - tsconfig = ":tsconfig", -)
diff --git a/packages/concatjs/test/tsconfig_extends/tsconfig-base.json b/packages/concatjs/test/tsconfig_extends/tsconfig-base.json deleted file mode 100644 index 639961e..0000000 --- a/packages/concatjs/test/tsconfig_extends/tsconfig-base.json +++ /dev/null
@@ -1,3 +0,0 @@ -{ - "extends": "./tsconfig-root" -}
diff --git a/packages/concatjs/test/tsconfig_extends/tsconfig-root.json b/packages/concatjs/test/tsconfig_extends/tsconfig-root.json deleted file mode 100644 index 9aa1316..0000000 --- a/packages/concatjs/test/tsconfig_extends/tsconfig-root.json +++ /dev/null
@@ -1,9 +0,0 @@ -{ - "compilerOptions": { - "lib": ["es2015.promise", "es5"], - // Explicitly set types settings so typescript doesn't auto-discover types. - // If all types are discovered then all types need to be included as deps - // or typescript may error out with TS2688: Cannot find type definition file for 'foo'. - "types": [] - } -} \ No newline at end of file
diff --git a/packages/concatjs/test/tsconfig_extends/tsconfig.json b/packages/concatjs/test/tsconfig_extends/tsconfig.json deleted file mode 100644 index 712de29..0000000 --- a/packages/concatjs/test/tsconfig_extends/tsconfig.json +++ /dev/null
@@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig-base" -} -
diff --git a/packages/concatjs/test/tsconfig_extends/uses_promise.ts b/packages/concatjs/test/tsconfig_extends/uses_promise.ts deleted file mode 100644 index 4d2e73b..0000000 --- a/packages/concatjs/test/tsconfig_extends/uses_promise.ts +++ /dev/null
@@ -1,3 +0,0 @@ -export function hiLater(): Promise<string> { - return Promise.resolve('hi'); -}
diff --git a/packages/concatjs/third_party/github.com/bazelbuild/bazel/src/main/protobuf/BUILD.bazel b/packages/concatjs/third_party/github.com/bazelbuild/bazel/src/main/protobuf/BUILD.bazel deleted file mode 100644 index 5e6ed7c..0000000 --- a/packages/concatjs/third_party/github.com/bazelbuild/bazel/src/main/protobuf/BUILD.bazel +++ /dev/null
@@ -1,42 +0,0 @@ -# BEGIN-DEV-ONLY -# Parts of this BUILD file only necessary when building within the bazelbuild/rules_typescript repo. -# The generated `@bazel/typescript` npm package contains a trimmed BUILD file using # DEV-ONLY fences. -load("@io_bazel_rules_go//go:def.bzl", "go_library") -load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") -load("@rules_proto//proto:defs.bzl", "proto_library") - -package(default_visibility = ["//:__pkg__"]) - -licenses(["notice"]) # Apache 2.0 -# END-DEV-ONLY - -# Export the raw proto file so protobufjs can be used reflectively without codegen -exports_files(["worker_protocol.proto"]) - -# BEGIN-DEV-ONLY -proto_library( - name = "blaze_worker_proto", - srcs = ["worker_protocol.proto"], - visibility = ["//visibility:public"], -) - -go_proto_library( - name = "blaze_worker_go_proto", - importpath = "github.com/bazelbuild/rules_typescript/third_party/github.com/bazelbuild/bazel/src/main/protobuf", - proto = ":blaze_worker_proto", - visibility = ["//visibility:public"], -) - -go_library( - name = "go_default_library", - embed = [":blaze_worker_go_proto"], - importpath = "github.com/bazelbuild/rules_typescript/third_party/github.com/bazelbuild/bazel/src/main/protobuf", - visibility = ["//visibility:public"], -) - -filegroup( - name = "npm_package_assets", - srcs = glob(["*"]), - visibility = ["//visibility:public"], -) -# END-DEV-ONLY
diff --git a/packages/concatjs/third_party/github.com/bazelbuild/bazel/src/main/protobuf/LICENSE b/packages/concatjs/third_party/github.com/bazelbuild/bazel/src/main/protobuf/LICENSE deleted file mode 100644 index 7a4a3ea..0000000 --- a/packages/concatjs/third_party/github.com/bazelbuild/bazel/src/main/protobuf/LICENSE +++ /dev/null
@@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file
diff --git a/packages/concatjs/third_party/github.com/bazelbuild/bazel/src/main/protobuf/worker_protocol.proto b/packages/concatjs/third_party/github.com/bazelbuild/bazel/src/main/protobuf/worker_protocol.proto deleted file mode 100644 index c628b7e..0000000 --- a/packages/concatjs/third_party/github.com/bazelbuild/bazel/src/main/protobuf/worker_protocol.proto +++ /dev/null
@@ -1,62 +0,0 @@ -// Copyright 2015 The Bazel Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package blaze.worker; - -option java_package = "com.google.devtools.build.lib.worker"; - -// An input file. -message Input { - // The path in the file system where to read this input artifact from. This is - // either a path relative to the execution root (the worker process is - // launched with the working directory set to the execution root), or an - // absolute path. - string path = 1; - - // A hash-value of the contents. The format of the contents is unspecified and - // the digest should be treated as an opaque token. - bytes digest = 2; -} - -// This represents a single work unit that Blaze sends to the worker. -message WorkRequest { - repeated string arguments = 1; - - // The inputs that the worker is allowed to read during execution of this - // request. - repeated Input inputs = 2; - - // To support multiplex worker, each WorkRequest must have an unique ID. This - // ID should be attached unchanged to the WorkResponse. - int32 request_id = 3; -} - -// The worker sends this message to Blaze when it finished its work on the -// WorkRequest message. -message WorkResponse { - int32 exit_code = 1; - - // This is printed to the user after the WorkResponse has been received and is - // supposed to contain compiler warnings / errors etc. - thus we'll use a - // string type here, which gives us UTF-8 encoding. - string output = 2; - - // To support multiplex worker, each WorkResponse must have an unique ID. - // Since worker processes which support multiplex worker will handle multiple - // WorkRequests in parallel, this ID will be used to determined which - // WorkerProxy does this WorkResponse belong to. - int32 request_id = 3; -}
diff --git a/packages/concatjs/third_party/npm/requirejs/BUILD b/packages/concatjs/third_party/npm/requirejs/BUILD deleted file mode 100644 index 11a14f8..0000000 --- a/packages/concatjs/third_party/npm/requirejs/BUILD +++ /dev/null
@@ -1,7 +0,0 @@ -exports_files(["require.js"]) - -filegroup( - name = "package_contents", - srcs = glob(["*"]), - visibility = ["//packages/concatjs:__pkg__"], -)
diff --git a/packages/concatjs/third_party/npm/requirejs/LICENSE b/packages/concatjs/third_party/npm/requirejs/LICENSE deleted file mode 100644 index 1e53e2c..0000000 --- a/packages/concatjs/third_party/npm/requirejs/LICENSE +++ /dev/null
@@ -1,45 +0,0 @@ -Copyright jQuery Foundation and other contributors, https://jquery.org/ - -This software consists of voluntary contributions made by many -individuals. For exact contribution history, see the revision history -available at https://github.com/requirejs/r.js - -The following license applies to all parts of this software except as -documented below: - -==== - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -==== - -Copyright and related rights for sample code are waived via CC0. Sample -code is defined as all source code displayed within the prose of the -documentation. - -CC0: http://creativecommons.org/publicdomain/zero/1.0/ - -==== - -Files located in the node_modules directory, and certain utilities used -to build or test the software in the tests, build/tests, env, lib directories, -are externally maintained libraries used by this software which have their own -licenses; we recommend you read them, as their terms may differ from the -terms above. \ No newline at end of file
diff --git a/packages/concatjs/third_party/npm/requirejs/require.js b/packages/concatjs/third_party/npm/requirejs/require.js deleted file mode 100644 index 09d21eb..0000000 --- a/packages/concatjs/third_party/npm/requirejs/require.js +++ /dev/null
@@ -1,2146 +0,0 @@ -// clang-format off -/** vim: et:ts=4:sw=4:sts=4 - * @license RequireJS 2.3.5 Copyright jQuery Foundation and other contributors. - * Released under MIT license, https://github.com/requirejs/requirejs/blob/master/LICENSE - */ -//Not using strict: uneven strict support in browsers, #392, and causes -//problems with requirejs.exec()/transpiler plugins that may not be strict. -/*jslint regexp: true, nomen: true, sloppy: true */ -/*global window, navigator, document, importScripts, setTimeout, opera */ - -var requirejs, require, define; -(function (global, setTimeout) { - var req, s, head, baseElement, dataMain, src, - interactiveScript, currentlyAddingScript, mainScript, subPath, - version = '2.3.5', - commentRegExp = /\/\*[\s\S]*?\*\/|([^:"'=]|^)\/\/.*$/mg, - cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g, - jsSuffixRegExp = /\.js$/, - currDirRegExp = /^\.\//, - op = Object.prototype, - ostring = op.toString, - hasOwn = op.hasOwnProperty, - isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document), - isWebWorker = !isBrowser && typeof importScripts !== 'undefined', - //PS3 indicates loaded and complete, but need to wait for complete - //specifically. Sequence is 'loading', 'loaded', execution, - // then 'complete'. The UA check is unfortunate, but not sure how - //to feature test w/o causing perf issues. - readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ? - /^complete$/ : /^(complete|loaded)$/, - defContextName = '_', - //Oh the tragedy, detecting opera. See the usage of isOpera for reason. - isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]', - contexts = {}, - cfg = {}, - globalDefQueue = [], - useInteractive = false; - - //Could match something like ')//comment', do not lose the prefix to comment. - function commentReplace(match, singlePrefix) { - return singlePrefix || ''; - } - - function isFunction(it) { - return ostring.call(it) === '[object Function]'; - } - - function isArray(it) { - return ostring.call(it) === '[object Array]'; - } - - /** - * Helper function for iterating over an array. If the func returns - * a true value, it will break out of the loop. - */ - function each(ary, func) { - if (ary) { - var i; - for (i = 0; i < ary.length; i += 1) { - if (ary[i] && func(ary[i], i, ary)) { - break; - } - } - } - } - - /** - * Helper function for iterating over an array backwards. If the func - * returns a true value, it will break out of the loop. - */ - function eachReverse(ary, func) { - if (ary) { - var i; - for (i = ary.length - 1; i > -1; i -= 1) { - if (ary[i] && func(ary[i], i, ary)) { - break; - } - } - } - } - - function hasProp(obj, prop) { - return hasOwn.call(obj, prop); - } - - function getOwn(obj, prop) { - return hasProp(obj, prop) && obj[prop]; - } - - /** - * Cycles over properties in an object and calls a function for each - * property value. If the function returns a truthy value, then the - * iteration is stopped. - */ - function eachProp(obj, func) { - var prop; - for (prop in obj) { - if (hasProp(obj, prop)) { - if (func(obj[prop], prop)) { - break; - } - } - } - } - - /** - * Simple function to mix in properties from source into target, - * but only if target does not already have a property of the same name. - */ - function mixin(target, source, force, deepStringMixin) { - if (source) { - eachProp(source, function (value, prop) { - if (force || !hasProp(target, prop)) { - if (deepStringMixin && typeof value === 'object' && value && - !isArray(value) && !isFunction(value) && - !(value instanceof RegExp)) { - - if (!target[prop]) { - target[prop] = {}; - } - mixin(target[prop], value, force, deepStringMixin); - } else { - target[prop] = value; - } - } - }); - } - return target; - } - - //Similar to Function.prototype.bind, but the 'this' object is specified - //first, since it is easier to read/figure out what 'this' will be. - function bind(obj, fn) { - return function () { - return fn.apply(obj, arguments); - }; - } - - function scripts() { - return document.getElementsByTagName('script'); - } - - function defaultOnError(err) { - throw err; - } - - //Allow getting a global that is expressed in - //dot notation, like 'a.b.c'. - function getGlobal(value) { - if (!value) { - return value; - } - var g = global; - each(value.split('.'), function (part) { - g = g[part]; - }); - return g; - } - - /** - * Constructs an error with a pointer to an URL with more information. - * @param {String} id the error ID that maps to an ID on a web page. - * @param {String} message human readable error. - * @param {Error} [err] the original error, if there is one. - * - * @returns {Error} - */ - function makeError(id, msg, err, requireModules) { - var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id); - e.requireType = id; - e.requireModules = requireModules; - if (err) { - e.originalError = err; - } - return e; - } - - if (typeof define !== 'undefined') { - //If a define is already in play via another AMD loader, - //do not overwrite. - return; - } - - if (typeof requirejs !== 'undefined') { - if (isFunction(requirejs)) { - //Do not overwrite an existing requirejs instance. - return; - } - cfg = requirejs; - requirejs = undefined; - } - - //Allow for a require config object - if (typeof require !== 'undefined' && !isFunction(require)) { - //assume it is a config object. - cfg = require; - require = undefined; - } - - function newContext(contextName) { - var inCheckLoaded, Module, context, handlers, - checkLoadedTimeoutId, - config = { - //Defaults. Do not set a default for map - //config to speed up normalize(), which - //will run faster if there is no default. - waitSeconds: 7, - baseUrl: './', - paths: {}, - bundles: {}, - pkgs: {}, - shim: {}, - config: {} - }, - registry = {}, - //registry of just enabled modules, to speed - //cycle breaking code when lots of modules - //are registered, but not activated. - enabledRegistry = {}, - undefEvents = {}, - defQueue = [], - defined = {}, - urlFetched = {}, - bundlesMap = {}, - requireCounter = 1, - unnormalizedCounter = 1; - - /** - * Trims the . and .. from an array of path segments. - * It will keep a leading path segment if a .. will become - * the first path segment, to help with module name lookups, - * which act like paths, but can be remapped. But the end result, - * all paths that use this function should look normalized. - * NOTE: this method MODIFIES the input array. - * @param {Array} ary the array of path segments. - */ - function trimDots(ary) { - var i, part; - for (i = 0; i < ary.length; i++) { - part = ary[i]; - if (part === '.') { - ary.splice(i, 1); - i -= 1; - } else if (part === '..') { - // If at the start, or previous value is still .., - // keep them so that when converted to a path it may - // still work when converted to a path, even though - // as an ID it is less than ideal. In larger point - // releases, may be better to just kick out an error. - if (i === 0 || (i === 1 && ary[2] === '..') || ary[i - 1] === '..') { - continue; - } else if (i > 0) { - ary.splice(i - 1, 2); - i -= 2; - } - } - } - } - - /** - * Given a relative module name, like ./something, normalize it to - * a real name that can be mapped to a path. - * @param {String} name the relative name - * @param {String} baseName a real name that the name arg is relative - * to. - * @param {Boolean} applyMap apply the map config to the value. Should - * only be done if this normalization is for a dependency ID. - * @returns {String} normalized name - */ - function normalize(name, baseName, applyMap) { - var pkgMain, mapValue, nameParts, i, j, nameSegment, lastIndex, - foundMap, foundI, foundStarMap, starI, normalizedBaseParts, - baseParts = (baseName && baseName.split('/')), - map = config.map, - starMap = map && map['*']; - - //Adjust any relative paths. - if (name) { - name = name.split('/'); - lastIndex = name.length - 1; - - // If wanting node ID compatibility, strip .js from end - // of IDs. Have to do this here, and not in nameToUrl - // because node allows either .js or non .js to map - // to same file. - if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) { - name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, ''); - } - - // Starts with a '.' so need the baseName - if (name[0].charAt(0) === '.' && baseParts) { - //Convert baseName to array, and lop off the last part, - //so that . matches that 'directory' and not name of the baseName's - //module. For instance, baseName of 'one/two/three', maps to - //'one/two/three.js', but we want the directory, 'one/two' for - //this normalization. - normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); - name = normalizedBaseParts.concat(name); - } - - trimDots(name); - name = name.join('/'); - } - - //Apply map config if available. - if (applyMap && map && (baseParts || starMap)) { - nameParts = name.split('/'); - - outerLoop: for (i = nameParts.length; i > 0; i -= 1) { - nameSegment = nameParts.slice(0, i).join('/'); - - if (baseParts) { - //Find the longest baseName segment match in the config. - //So, do joins on the biggest to smallest lengths of baseParts. - for (j = baseParts.length; j > 0; j -= 1) { - mapValue = getOwn(map, baseParts.slice(0, j).join('/')); - - //baseName segment has config, find if it has one for - //this name. - if (mapValue) { - mapValue = getOwn(mapValue, nameSegment); - if (mapValue) { - //Match, update name to the new value. - foundMap = mapValue; - foundI = i; - break outerLoop; - } - } - } - } - - //Check for a star map match, but just hold on to it, - //if there is a shorter segment match later in a matching - //config, then favor over this star map. - if (!foundStarMap && starMap && getOwn(starMap, nameSegment)) { - foundStarMap = getOwn(starMap, nameSegment); - starI = i; - } - } - - if (!foundMap && foundStarMap) { - foundMap = foundStarMap; - foundI = starI; - } - - if (foundMap) { - nameParts.splice(0, foundI, foundMap); - name = nameParts.join('/'); - } - } - - // If the name points to a package's name, use - // the package main instead. - pkgMain = getOwn(config.pkgs, name); - - return pkgMain ? pkgMain : name; - } - - function removeScript(name) { - if (isBrowser) { - each(scripts(), function (scriptNode) { - if (scriptNode.getAttribute('data-requiremodule') === name && - scriptNode.getAttribute('data-requirecontext') === context.contextName) { - scriptNode.parentNode.removeChild(scriptNode); - return true; - } - }); - } - } - - function hasPathFallback(id) { - var pathConfig = getOwn(config.paths, id); - if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) { - //Pop off the first array value, since it failed, and - //retry - pathConfig.shift(); - context.require.undef(id); - - //Custom require that does not do map translation, since - //ID is "absolute", already mapped/resolved. - context.makeRequire(null, { - skipMap: true - })([id]); - - return true; - } - } - - //Turns a plugin!resource to [plugin, resource] - //with the plugin being undefined if the name - //did not have a plugin prefix. - function splitPrefix(name) { - var prefix, - index = name ? name.indexOf('!') : -1; - if (index > -1) { - prefix = name.substring(0, index); - name = name.substring(index + 1, name.length); - } - return [prefix, name]; - } - - /** - * Creates a module mapping that includes plugin prefix, module - * name, and path. If parentModuleMap is provided it will - * also normalize the name via require.normalize() - * - * @param {String} name the module name - * @param {String} [parentModuleMap] parent module map - * for the module name, used to resolve relative names. - * @param {Boolean} isNormalized: is the ID already normalized. - * This is true if this call is done for a define() module ID. - * @param {Boolean} applyMap: apply the map config to the ID. - * Should only be true if this map is for a dependency. - * - * @returns {Object} - */ - function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) { - var url, pluginModule, suffix, nameParts, - prefix = null, - parentName = parentModuleMap ? parentModuleMap.name : null, - originalName = name, - isDefine = true, - normalizedName = ''; - - //If no name, then it means it is a require call, generate an - //internal name. - if (!name) { - isDefine = false; - name = '_@r' + (requireCounter += 1); - } - - nameParts = splitPrefix(name); - prefix = nameParts[0]; - name = nameParts[1]; - - if (prefix) { - prefix = normalize(prefix, parentName, applyMap); - pluginModule = getOwn(defined, prefix); - } - - //Account for relative paths if there is a base name. - if (name) { - if (prefix) { - if (isNormalized) { - normalizedName = name; - } else if (pluginModule && pluginModule.normalize) { - //Plugin is loaded, use its normalize method. - normalizedName = pluginModule.normalize(name, function (name) { - return normalize(name, parentName, applyMap); - }); - } else { - // If nested plugin references, then do not try to - // normalize, as it will not normalize correctly. This - // places a restriction on resourceIds, and the longer - // term solution is not to normalize until plugins are - // loaded and all normalizations to allow for async - // loading of a loader plugin. But for now, fixes the - // common uses. Details in #1131 - normalizedName = name.indexOf('!') === -1 ? - normalize(name, parentName, applyMap) : - name; - } - } else { - //A regular module. - normalizedName = normalize(name, parentName, applyMap); - - //Normalized name may be a plugin ID due to map config - //application in normalize. The map config values must - //already be normalized, so do not need to redo that part. - nameParts = splitPrefix(normalizedName); - prefix = nameParts[0]; - normalizedName = nameParts[1]; - isNormalized = true; - - url = context.nameToUrl(normalizedName); - } - } - - //If the id is a plugin id that cannot be determined if it needs - //normalization, stamp it with a unique ID so two matching relative - //ids that may conflict can be separate. - suffix = prefix && !pluginModule && !isNormalized ? - '_unnormalized' + (unnormalizedCounter += 1) : - ''; - - return { - prefix: prefix, - name: normalizedName, - parentMap: parentModuleMap, - unnormalized: !!suffix, - url: url, - originalName: originalName, - isDefine: isDefine, - id: (prefix ? - prefix + '!' + normalizedName : - normalizedName) + suffix - }; - } - - function getModule(depMap) { - var id = depMap.id, - mod = getOwn(registry, id); - - if (!mod) { - mod = registry[id] = new context.Module(depMap); - } - - return mod; - } - - function on(depMap, name, fn) { - var id = depMap.id, - mod = getOwn(registry, id); - - if (hasProp(defined, id) && - (!mod || mod.defineEmitComplete)) { - if (name === 'defined') { - fn(defined[id]); - } - } else { - mod = getModule(depMap); - if (mod.error && name === 'error') { - fn(mod.error); - } else { - mod.on(name, fn); - } - } - } - - function onError(err, errback) { - var ids = err.requireModules, - notified = false; - - if (errback) { - errback(err); - } else { - each(ids, function (id) { - var mod = getOwn(registry, id); - if (mod) { - //Set error on module, so it skips timeout checks. - mod.error = err; - if (mod.events.error) { - notified = true; - mod.emit('error', err); - } - } - }); - - if (!notified) { - req.onError(err); - } - } - } - - /** - * Internal method to transfer globalQueue items to this context's - * defQueue. - */ - function takeGlobalQueue() { - //Push all the globalDefQueue items into the context's defQueue - if (globalDefQueue.length) { - each(globalDefQueue, function(queueItem) { - var id = queueItem[0]; - if (typeof id === 'string') { - context.defQueueMap[id] = true; - } - defQueue.push(queueItem); - }); - globalDefQueue = []; - } - } - - handlers = { - 'require': function (mod) { - if (mod.require) { - return mod.require; - } else { - return (mod.require = context.makeRequire(mod.map)); - } - }, - 'exports': function (mod) { - mod.usingExports = true; - if (mod.map.isDefine) { - if (mod.exports) { - return (defined[mod.map.id] = mod.exports); - } else { - return (mod.exports = defined[mod.map.id] = {}); - } - } - }, - 'module': function (mod) { - if (mod.module) { - return mod.module; - } else { - return (mod.module = { - id: mod.map.id, - uri: mod.map.url, - config: function () { - return getOwn(config.config, mod.map.id) || {}; - }, - exports: mod.exports || (mod.exports = {}) - }); - } - } - }; - - function cleanRegistry(id) { - //Clean up machinery used for waiting modules. - delete registry[id]; - delete enabledRegistry[id]; - } - - function breakCycle(mod, traced, processed) { - var id = mod.map.id; - - if (mod.error) { - mod.emit('error', mod.error); - } else { - traced[id] = true; - each(mod.depMaps, function (depMap, i) { - var depId = depMap.id, - dep = getOwn(registry, depId); - - //Only force things that have not completed - //being defined, so still in the registry, - //and only if it has not been matched up - //in the module already. - if (dep && !mod.depMatched[i] && !processed[depId]) { - if (getOwn(traced, depId)) { - mod.defineDep(i, defined[depId]); - mod.check(); //pass false? - } else { - breakCycle(dep, traced, processed); - } - } - }); - processed[id] = true; - } - } - - function checkLoaded() { - var err, usingPathFallback, - waitInterval = config.waitSeconds * 1000, - //It is possible to disable the wait interval by using waitSeconds of 0. - expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(), - noLoads = [], - reqCalls = [], - stillLoading = false, - needCycleCheck = true; - - //Do not bother if this call was a result of a cycle break. - if (inCheckLoaded) { - return; - } - - inCheckLoaded = true; - - //Figure out the state of all the modules. - eachProp(enabledRegistry, function (mod) { - var map = mod.map, - modId = map.id; - - //Skip things that are not enabled or in error state. - if (!mod.enabled) { - return; - } - - if (!map.isDefine) { - reqCalls.push(mod); - } - - if (!mod.error) { - //If the module should be executed, and it has not - //been inited and time is up, remember it. - if (!mod.inited && expired) { - if (hasPathFallback(modId)) { - usingPathFallback = true; - stillLoading = true; - } else { - noLoads.push(modId); - removeScript(modId); - } - } else if (!mod.inited && mod.fetched && map.isDefine) { - stillLoading = true; - if (!map.prefix) { - //No reason to keep looking for unfinished - //loading. If the only stillLoading is a - //plugin resource though, keep going, - //because it may be that a plugin resource - //is waiting on a non-plugin cycle. - return (needCycleCheck = false); - } - } - } - }); - - if (expired && noLoads.length) { - //If wait time expired, throw error of unloaded modules. - err = makeError('timeout', 'Load timeout for modules: ' + noLoads, null, noLoads); - err.contextName = context.contextName; - return onError(err); - } - - //Not expired, check for a cycle. - if (needCycleCheck) { - each(reqCalls, function (mod) { - breakCycle(mod, {}, {}); - }); - } - - //If still waiting on loads, and the waiting load is something - //other than a plugin resource, or there are still outstanding - //scripts, then just try back later. - if ((!expired || usingPathFallback) && stillLoading) { - //Something is still waiting to load. Wait for it, but only - //if a timeout is not already in effect. - if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) { - checkLoadedTimeoutId = setTimeout(function () { - checkLoadedTimeoutId = 0; - checkLoaded(); - }, 50); - } - } - - inCheckLoaded = false; - } - - Module = function (map) { - this.events = getOwn(undefEvents, map.id) || {}; - this.map = map; - this.shim = getOwn(config.shim, map.id); - this.depExports = []; - this.depMaps = []; - this.depMatched = []; - this.pluginMaps = {}; - this.depCount = 0; - - /* this.exports this.factory - this.depMaps = [], - this.enabled, this.fetched - */ - }; - - Module.prototype = { - init: function (depMaps, factory, errback, options) { - options = options || {}; - - //Do not do more inits if already done. Can happen if there - //are multiple define calls for the same module. That is not - //a normal, common case, but it is also not unexpected. - if (this.inited) { - return; - } - - this.factory = factory; - - if (errback) { - //Register for errors on this module. - this.on('error', errback); - } else if (this.events.error) { - //If no errback already, but there are error listeners - //on this module, set up an errback to pass to the deps. - errback = bind(this, function (err) { - this.emit('error', err); - }); - } - - //Do a copy of the dependency array, so that - //source inputs are not modified. For example - //"shim" deps are passed in here directly, and - //doing a direct modification of the depMaps array - //would affect that config. - this.depMaps = depMaps && depMaps.slice(0); - - this.errback = errback; - - //Indicate this module has be initialized - this.inited = true; - - this.ignore = options.ignore; - - //Could have option to init this module in enabled mode, - //or could have been previously marked as enabled. However, - //the dependencies are not known until init is called. So - //if enabled previously, now trigger dependencies as enabled. - if (options.enabled || this.enabled) { - //Enable this module and dependencies. - //Will call this.check() - this.enable(); - } else { - this.check(); - } - }, - - defineDep: function (i, depExports) { - //Because of cycles, defined callback for a given - //export can be called more than once. - if (!this.depMatched[i]) { - this.depMatched[i] = true; - this.depCount -= 1; - this.depExports[i] = depExports; - } - }, - - fetch: function () { - if (this.fetched) { - return; - } - this.fetched = true; - - context.startTime = (new Date()).getTime(); - - var map = this.map; - - //If the manager is for a plugin managed resource, - //ask the plugin to load it now. - if (this.shim) { - context.makeRequire(this.map, { - enableBuildCallback: true - })(this.shim.deps || [], bind(this, function () { - return map.prefix ? this.callPlugin() : this.load(); - })); - } else { - //Regular dependency. - return map.prefix ? this.callPlugin() : this.load(); - } - }, - - load: function () { - var url = this.map.url; - - //Regular dependency. - if (!urlFetched[url]) { - urlFetched[url] = true; - context.load(this.map.id, url); - } - }, - - /** - * Checks if the module is ready to define itself, and if so, - * define it. - */ - check: function () { - if (!this.enabled || this.enabling) { - return; - } - - var err, cjsModule, - id = this.map.id, - depExports = this.depExports, - exports = this.exports, - factory = this.factory; - - if (!this.inited) { - // Only fetch if not already in the defQueue. - if (!hasProp(context.defQueueMap, id)) { - this.fetch(); - } - } else if (this.error) { - this.emit('error', this.error); - } else if (!this.defining) { - //The factory could trigger another require call - //that would result in checking this module to - //define itself again. If already in the process - //of doing that, skip this work. - this.defining = true; - - if (this.depCount < 1 && !this.defined) { - if (isFunction(factory)) { - //If there is an error listener, favor passing - //to that instead of throwing an error. However, - //only do it for define()'d modules. require - //errbacks should not be called for failures in - //their callbacks (#699). However if a global - //onError is set, use that. - if ((this.events.error && this.map.isDefine) || - req.onError !== defaultOnError) { - try { - exports = context.execCb(id, factory, depExports, exports); - } catch (e) { - err = e; - } - } else { - exports = context.execCb(id, factory, depExports, exports); - } - - // Favor return value over exports. If node/cjs in play, - // then will not have a return value anyway. Favor - // module.exports assignment over exports object. - if (this.map.isDefine && exports === undefined) { - cjsModule = this.module; - if (cjsModule) { - exports = cjsModule.exports; - } else if (this.usingExports) { - //exports already set the defined value. - exports = this.exports; - } - } - - if (err) { - err.requireMap = this.map; - err.requireModules = this.map.isDefine ? [this.map.id] : null; - err.requireType = this.map.isDefine ? 'define' : 'require'; - return onError((this.error = err)); - } - - } else { - //Just a literal value - exports = factory; - } - - this.exports = exports; - - if (this.map.isDefine && !this.ignore) { - defined[id] = exports; - - if (req.onResourceLoad) { - var resLoadMaps = []; - each(this.depMaps, function (depMap) { - resLoadMaps.push(depMap.normalizedMap || depMap); - }); - req.onResourceLoad(context, this.map, resLoadMaps); - } - } - - //Clean up - cleanRegistry(id); - - this.defined = true; - } - - //Finished the define stage. Allow calling check again - //to allow define notifications below in the case of a - //cycle. - this.defining = false; - - if (this.defined && !this.defineEmitted) { - this.defineEmitted = true; - this.emit('defined', this.exports); - this.defineEmitComplete = true; - } - - } - }, - - callPlugin: function () { - var map = this.map, - id = map.id, - //Map already normalized the prefix. - pluginMap = makeModuleMap(map.prefix); - - //Mark this as a dependency for this plugin, so it - //can be traced for cycles. - this.depMaps.push(pluginMap); - - on(pluginMap, 'defined', bind(this, function (plugin) { - var load, normalizedMap, normalizedMod, - bundleId = getOwn(bundlesMap, this.map.id), - name = this.map.name, - parentName = this.map.parentMap ? this.map.parentMap.name : null, - localRequire = context.makeRequire(map.parentMap, { - enableBuildCallback: true - }); - - //If current map is not normalized, wait for that - //normalized name to load instead of continuing. - if (this.map.unnormalized) { - //Normalize the ID if the plugin allows it. - if (plugin.normalize) { - name = plugin.normalize(name, function (name) { - return normalize(name, parentName, true); - }) || ''; - } - - //prefix and name should already be normalized, no need - //for applying map config again either. - normalizedMap = makeModuleMap(map.prefix + '!' + name, - this.map.parentMap, - true); - on(normalizedMap, - 'defined', bind(this, function (value) { - this.map.normalizedMap = normalizedMap; - this.init([], function () { return value; }, null, { - enabled: true, - ignore: true - }); - })); - - normalizedMod = getOwn(registry, normalizedMap.id); - if (normalizedMod) { - //Mark this as a dependency for this plugin, so it - //can be traced for cycles. - this.depMaps.push(normalizedMap); - - if (this.events.error) { - normalizedMod.on('error', bind(this, function (err) { - this.emit('error', err); - })); - } - normalizedMod.enable(); - } - - return; - } - - //If a paths config, then just load that file instead to - //resolve the plugin, as it is built into that paths layer. - if (bundleId) { - this.map.url = context.nameToUrl(bundleId); - this.load(); - return; - } - - load = bind(this, function (value) { - this.init([], function () { return value; }, null, { - enabled: true - }); - }); - - load.error = bind(this, function (err) { - this.inited = true; - this.error = err; - err.requireModules = [id]; - - //Remove temp unnormalized modules for this module, - //since they will never be resolved otherwise now. - eachProp(registry, function (mod) { - if (mod.map.id.indexOf(id + '_unnormalized') === 0) { - cleanRegistry(mod.map.id); - } - }); - - onError(err); - }); - - //Allow plugins to load other code without having to know the - //context or how to 'complete' the load. - load.fromText = bind(this, function (text, textAlt) { - /*jslint evil: true */ - var moduleName = map.name, - moduleMap = makeModuleMap(moduleName), - hasInteractive = useInteractive; - - //As of 2.1.0, support just passing the text, to reinforce - //fromText only being called once per resource. Still - //support old style of passing moduleName but discard - //that moduleName in favor of the internal ref. - if (textAlt) { - text = textAlt; - } - - //Turn off interactive script matching for IE for any define - //calls in the text, then turn it back on at the end. - if (hasInteractive) { - useInteractive = false; - } - - //Prime the system by creating a module instance for - //it. - getModule(moduleMap); - - //Transfer any config to this other module. - if (hasProp(config.config, id)) { - config.config[moduleName] = config.config[id]; - } - - try { - req.exec(text); - } catch (e) { - return onError(makeError('fromtexteval', - 'fromText eval for ' + id + - ' failed: ' + e, - e, - [id])); - } - - if (hasInteractive) { - useInteractive = true; - } - - //Mark this as a dependency for the plugin - //resource - this.depMaps.push(moduleMap); - - //Support anonymous modules. - context.completeLoad(moduleName); - - //Bind the value of that module to the value for this - //resource ID. - localRequire([moduleName], load); - }); - - //Use parentName here since the plugin's name is not reliable, - //could be some weird string with no path that actually wants to - //reference the parentName's path. - plugin.load(map.name, localRequire, load, config); - })); - - context.enable(pluginMap, this); - this.pluginMaps[pluginMap.id] = pluginMap; - }, - - enable: function () { - enabledRegistry[this.map.id] = this; - this.enabled = true; - - //Set flag mentioning that the module is enabling, - //so that immediate calls to the defined callbacks - //for dependencies do not trigger inadvertent load - //with the depCount still being zero. - this.enabling = true; - - //Enable each dependency - each(this.depMaps, bind(this, function (depMap, i) { - var id, mod, handler; - - if (typeof depMap === 'string') { - //Dependency needs to be converted to a depMap - //and wired up to this module. - depMap = makeModuleMap(depMap, - (this.map.isDefine ? this.map : this.map.parentMap), - false, - !this.skipMap); - this.depMaps[i] = depMap; - - handler = getOwn(handlers, depMap.id); - - if (handler) { - this.depExports[i] = handler(this); - return; - } - - this.depCount += 1; - - on(depMap, 'defined', bind(this, function (depExports) { - if (this.undefed) { - return; - } - this.defineDep(i, depExports); - this.check(); - })); - - if (this.errback) { - on(depMap, 'error', bind(this, this.errback)); - } else if (this.events.error) { - // No direct errback on this module, but something - // else is listening for errors, so be sure to - // propagate the error correctly. - on(depMap, 'error', bind(this, function(err) { - this.emit('error', err); - })); - } - } - - id = depMap.id; - mod = registry[id]; - - //Skip special modules like 'require', 'exports', 'module' - //Also, don't call enable if it is already enabled, - //important in circular dependency cases. - if (!hasProp(handlers, id) && mod && !mod.enabled) { - context.enable(depMap, this); - } - })); - - //Enable each plugin that is used in - //a dependency - eachProp(this.pluginMaps, bind(this, function (pluginMap) { - var mod = getOwn(registry, pluginMap.id); - if (mod && !mod.enabled) { - context.enable(pluginMap, this); - } - })); - - this.enabling = false; - - this.check(); - }, - - on: function (name, cb) { - var cbs = this.events[name]; - if (!cbs) { - cbs = this.events[name] = []; - } - cbs.push(cb); - }, - - emit: function (name, evt) { - each(this.events[name], function (cb) { - cb(evt); - }); - if (name === 'error') { - //Now that the error handler was triggered, remove - //the listeners, since this broken Module instance - //can stay around for a while in the registry. - delete this.events[name]; - } - } - }; - - function callGetModule(args) { - //Skip modules already defined. - if (!hasProp(defined, args[0])) { - getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]); - } - } - - function removeListener(node, func, name, ieName) { - //Favor detachEvent because of IE9 - //issue, see attachEvent/addEventListener comment elsewhere - //in this file. - if (node.detachEvent && !isOpera) { - //Probably IE. If not it will throw an error, which will be - //useful to know. - if (ieName) { - node.detachEvent(ieName, func); - } - } else { - node.removeEventListener(name, func, false); - } - } - - /** - * Given an event from a script node, get the requirejs info from it, - * and then removes the event listeners on the node. - * @param {Event} evt - * @returns {Object} - */ - function getScriptData(evt) { - //Using currentTarget instead of target for Firefox 2.0's sake. Not - //all old browsers will be supported, but this one was easy enough - //to support and still makes sense. - var node = evt.currentTarget || evt.srcElement; - - //Remove the listeners once here. - removeListener(node, context.onScriptLoad, 'load', 'onreadystatechange'); - removeListener(node, context.onScriptError, 'error'); - - return { - node: node, - id: node && node.getAttribute('data-requiremodule') - }; - } - - function intakeDefines() { - var args; - - //Any defined modules in the global queue, intake them now. - takeGlobalQueue(); - - //Make sure any remaining defQueue items get properly processed. - while (defQueue.length) { - args = defQueue.shift(); - if (args[0] === null) { - return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + - args[args.length - 1])); - } else { - //args are id, deps, factory. Should be normalized by the - //define() function. - callGetModule(args); - } - } - context.defQueueMap = {}; - } - - context = { - config: config, - contextName: contextName, - registry: registry, - defined: defined, - urlFetched: urlFetched, - defQueue: defQueue, - defQueueMap: {}, - Module: Module, - makeModuleMap: makeModuleMap, - nextTick: req.nextTick, - onError: onError, - - /** - * Set a configuration for the context. - * @param {Object} cfg config object to integrate. - */ - configure: function (cfg) { - //Make sure the baseUrl ends in a slash. - if (cfg.baseUrl) { - if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') { - cfg.baseUrl += '/'; - } - } - - // Convert old style urlArgs string to a function. - if (typeof cfg.urlArgs === 'string') { - var urlArgs = cfg.urlArgs; - cfg.urlArgs = function(id, url) { - return (url.indexOf('?') === -1 ? '?' : '&') + urlArgs; - }; - } - - //Save off the paths since they require special processing, - //they are additive. - var shim = config.shim, - objs = { - paths: true, - bundles: true, - config: true, - map: true - }; - - eachProp(cfg, function (value, prop) { - if (objs[prop]) { - if (!config[prop]) { - config[prop] = {}; - } - mixin(config[prop], value, true, true); - } else { - config[prop] = value; - } - }); - - //Reverse map the bundles - if (cfg.bundles) { - eachProp(cfg.bundles, function (value, prop) { - each(value, function (v) { - if (v !== prop) { - bundlesMap[v] = prop; - } - }); - }); - } - - //Merge shim - if (cfg.shim) { - eachProp(cfg.shim, function (value, id) { - //Normalize the structure - if (isArray(value)) { - value = { - deps: value - }; - } - if ((value.exports || value.init) && !value.exportsFn) { - value.exportsFn = context.makeShimExports(value); - } - shim[id] = value; - }); - config.shim = shim; - } - - //Adjust packages if necessary. - if (cfg.packages) { - each(cfg.packages, function (pkgObj) { - var location, name; - - pkgObj = typeof pkgObj === 'string' ? {name: pkgObj} : pkgObj; - - name = pkgObj.name; - location = pkgObj.location; - if (location) { - config.paths[name] = pkgObj.location; - } - - //Save pointer to main module ID for pkg name. - //Remove leading dot in main, so main paths are normalized, - //and remove any trailing .js, since different package - //envs have different conventions: some use a module name, - //some use a file name. - config.pkgs[name] = pkgObj.name + '/' + (pkgObj.main || 'main') - .replace(currDirRegExp, '') - .replace(jsSuffixRegExp, ''); - }); - } - - //If there are any "waiting to execute" modules in the registry, - //update the maps for them, since their info, like URLs to load, - //may have changed. - eachProp(registry, function (mod, id) { - //If module already has init called, since it is too - //late to modify them, and ignore unnormalized ones - //since they are transient. - if (!mod.inited && !mod.map.unnormalized) { - mod.map = makeModuleMap(id, null, true); - } - }); - - //If a deps array or a config callback is specified, then call - //require with those args. This is useful when require is defined as a - //config object before require.js is loaded. - if (cfg.deps || cfg.callback) { - context.require(cfg.deps || [], cfg.callback); - } - }, - - makeShimExports: function (value) { - function fn() { - var ret; - if (value.init) { - ret = value.init.apply(global, arguments); - } - return ret || (value.exports && getGlobal(value.exports)); - } - return fn; - }, - - makeRequire: function (relMap, options) { - options = options || {}; - - function localRequire(deps, callback, errback) { - var id, map, requireMod; - - if (options.enableBuildCallback && callback && isFunction(callback)) { - callback.__requireJsBuild = true; - } - - if (typeof deps === 'string') { - if (isFunction(callback)) { - //Invalid call - return onError(makeError('requireargs', 'Invalid require call'), errback); - } - - //If require|exports|module are requested, get the - //value for them from the special handlers. Caveat: - //this only works while module is being defined. - if (relMap && hasProp(handlers, deps)) { - return handlers[deps](registry[relMap.id]); - } - - //Synchronous access to one module. If require.get is - //available (as in the Node adapter), prefer that. - if (req.get) { - return req.get(context, deps, relMap, localRequire); - } - - //Normalize module name, if it contains . or .. - map = makeModuleMap(deps, relMap, false, true); - id = map.id; - - if (!hasProp(defined, id)) { - return onError(makeError('notloaded', 'Module name "' + - id + - '" has not been loaded yet for context: ' + - contextName + - (relMap ? '' : '. Use require([])'))); - } - return defined[id]; - } - - //Grab defines waiting in the global queue. - intakeDefines(); - - //Mark all the dependencies as needing to be loaded. - context.nextTick(function () { - //Some defines could have been added since the - //require call, collect them. - intakeDefines(); - - requireMod = getModule(makeModuleMap(null, relMap)); - - //Store if map config should be applied to this require - //call for dependencies. - requireMod.skipMap = options.skipMap; - - requireMod.init(deps, callback, errback, { - enabled: true - }); - - checkLoaded(); - }); - - return localRequire; - } - - mixin(localRequire, { - isBrowser: isBrowser, - - /** - * Converts a module name + .extension into an URL path. - * *Requires* the use of a module name. It does not support using - * plain URLs like nameToUrl. - */ - toUrl: function (moduleNamePlusExt) { - var ext, - index = moduleNamePlusExt.lastIndexOf('.'), - segment = moduleNamePlusExt.split('/')[0], - isRelative = segment === '.' || segment === '..'; - - //Have a file extension alias, and it is not the - //dots from a relative path. - if (index !== -1 && (!isRelative || index > 1)) { - ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length); - moduleNamePlusExt = moduleNamePlusExt.substring(0, index); - } - - return context.nameToUrl(normalize(moduleNamePlusExt, - relMap && relMap.id, true), ext, true); - }, - - defined: function (id) { - return hasProp(defined, makeModuleMap(id, relMap, false, true).id); - }, - - specified: function (id) { - id = makeModuleMap(id, relMap, false, true).id; - return hasProp(defined, id) || hasProp(registry, id); - } - }); - - //Only allow undef on top level require calls - if (!relMap) { - localRequire.undef = function (id) { - //Bind any waiting define() calls to this context, - //fix for #408 - takeGlobalQueue(); - - var map = makeModuleMap(id, relMap, true), - mod = getOwn(registry, id); - - mod.undefed = true; - removeScript(id); - - delete defined[id]; - delete urlFetched[map.url]; - delete undefEvents[id]; - - //Clean queued defines too. Go backwards - //in array so that the splices do not - //mess up the iteration. - eachReverse(defQueue, function(args, i) { - if (args[0] === id) { - defQueue.splice(i, 1); - } - }); - delete context.defQueueMap[id]; - - if (mod) { - //Hold on to listeners in case the - //module will be attempted to be reloaded - //using a different config. - if (mod.events.defined) { - undefEvents[id] = mod.events; - } - - cleanRegistry(id); - } - }; - } - - return localRequire; - }, - - /** - * Called to enable a module if it is still in the registry - * awaiting enablement. A second arg, parent, the parent module, - * is passed in for context, when this method is overridden by - * the optimizer. Not shown here to keep code compact. - */ - enable: function (depMap) { - var mod = getOwn(registry, depMap.id); - if (mod) { - getModule(depMap).enable(); - } - }, - - /** - * Internal method used by environment adapters to complete a load event. - * A load event could be a script load or just a load pass from a synchronous - * load call. - * @param {String} moduleName the name of the module to potentially complete. - */ - completeLoad: function (moduleName) { - var found, args, mod, - shim = getOwn(config.shim, moduleName) || {}, - shExports = shim.exports; - - takeGlobalQueue(); - - while (defQueue.length) { - args = defQueue.shift(); - if (args[0] === null) { - args[0] = moduleName; - //If already found an anonymous module and bound it - //to this name, then this is some other anon module - //waiting for its completeLoad to fire. - if (found) { - break; - } - found = true; - } else if (args[0] === moduleName) { - //Found matching define call for this script! - found = true; - } - - callGetModule(args); - } - context.defQueueMap = {}; - - //Do this after the cycle of callGetModule in case the result - //of those calls/init calls changes the registry. - mod = getOwn(registry, moduleName); - - if (!found && !hasProp(defined, moduleName) && mod && !mod.inited) { - if (config.enforceDefine && (!shExports || !getGlobal(shExports))) { - if (hasPathFallback(moduleName)) { - return; - } else { - return onError(makeError('nodefine', - 'No define call for ' + moduleName, - null, - [moduleName])); - } - } else { - //A script that does not call define(), so just simulate - //the call for it. - callGetModule([moduleName, (shim.deps || []), shim.exportsFn]); - } - } - - checkLoaded(); - }, - - /** - * Converts a module name to a file path. Supports cases where - * moduleName may actually be just an URL. - * Note that it **does not** call normalize on the moduleName, - * it is assumed to have already been normalized. This is an - * internal API, not a public one. Use toUrl for the public API. - */ - nameToUrl: function (moduleName, ext, skipExt) { - var paths, syms, i, parentModule, url, - parentPath, bundleId, - pkgMain = getOwn(config.pkgs, moduleName); - - if (pkgMain) { - moduleName = pkgMain; - } - - bundleId = getOwn(bundlesMap, moduleName); - - if (bundleId) { - return context.nameToUrl(bundleId, ext, skipExt); - } - - //If a colon is in the URL, it indicates a protocol is used and it is just - //an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?) - //or ends with .js, then assume the user meant to use an url and not a module id. - //The slash is important for protocol-less URLs as well as full paths. - if (req.jsExtRegExp.test(moduleName)) { - //Just a plain path, not module name lookup, so just return it. - //Add extension if it is included. This is a bit wonky, only non-.js things pass - //an extension, this method probably needs to be reworked. - url = moduleName + (ext || ''); - } else { - //A module that needs to be converted to a path. - paths = config.paths; - - syms = moduleName.split('/'); - //For each module name segment, see if there is a path - //registered for it. Start with most specific name - //and work up from it. - for (i = syms.length; i > 0; i -= 1) { - parentModule = syms.slice(0, i).join('/'); - - parentPath = getOwn(paths, parentModule); - if (parentPath) { - //If an array, it means there are a few choices, - //Choose the one that is desired - if (isArray(parentPath)) { - parentPath = parentPath[0]; - } - syms.splice(0, i, parentPath); - break; - } - } - - //Join the path parts together, then figure out if baseUrl is needed. - url = syms.join('/'); - url += (ext || (/^data\:|^blob\:|\?/.test(url) || skipExt ? '' : '.js')); - url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url; - } - - return config.urlArgs && !/^blob\:/.test(url) ? - url + config.urlArgs(moduleName, url) : url; - }, - - //Delegates to req.load. Broken out as a separate function to - //allow overriding in the optimizer. - load: function (id, url) { - req.load(context, id, url); - }, - - /** - * Executes a module callback function. Broken out as a separate function - * solely to allow the build system to sequence the files in the built - * layer in the right sequence. - * - * @private - */ - execCb: function (name, callback, args, exports) { - return callback.apply(exports, args); - }, - - /** - * callback for script loads, used to check status of loading. - * - * @param {Event} evt the event from the browser for the script - * that was loaded. - */ - onScriptLoad: function (evt) { - //Using currentTarget instead of target for Firefox 2.0's sake. Not - //all old browsers will be supported, but this one was easy enough - //to support and still makes sense. - if (evt.type === 'load' || - (readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) { - //Reset interactive script so a script node is not held onto for - //to long. - interactiveScript = null; - - //Pull out the name of the module and the context. - var data = getScriptData(evt); - context.completeLoad(data.id); - } - }, - - /** - * Callback for script errors. - */ - onScriptError: function (evt) { - var data = getScriptData(evt); - if (!hasPathFallback(data.id)) { - var parents = []; - eachProp(registry, function(value, key) { - if (key.indexOf('_@r') !== 0) { - each(value.depMaps, function(depMap) { - if (depMap.id === data.id) { - parents.push(key); - return true; - } - }); - } - }); - return onError(makeError('scripterror', 'Script error for "' + data.id + - (parents.length ? - '", needed by: ' + parents.join(', ') : - '"'), evt, [data.id])); - } - } - }; - - context.require = context.makeRequire(); - return context; - } - - /** - * Main entry point. - * - * If the only argument to require is a string, then the module that - * is represented by that string is fetched for the appropriate context. - * - * If the first argument is an array, then it will be treated as an array - * of dependency string names to fetch. An optional function callback can - * be specified to execute when all of those dependencies are available. - * - * Make a local req variable to help Caja compliance (it assumes things - * on a require that are not standardized), and to give a short - * name for minification/local scope use. - */ - req = requirejs = function (deps, callback, errback, optional) { - - //Find the right context, use default - var context, config, - contextName = defContextName; - - // Determine if have config object in the call. - if (!isArray(deps) && typeof deps !== 'string') { - // deps is a config object - config = deps; - if (isArray(callback)) { - // Adjust args if there are dependencies - deps = callback; - callback = errback; - errback = optional; - } else { - deps = []; - } - } - - if (config && config.context) { - contextName = config.context; - } - - context = getOwn(contexts, contextName); - if (!context) { - context = contexts[contextName] = req.s.newContext(contextName); - } - - if (config) { - context.configure(config); - } - - return context.require(deps, callback, errback); - }; - - /** - * Support require.config() to make it easier to cooperate with other - * AMD loaders on globally agreed names. - */ - req.config = function (config) { - return req(config); - }; - - /** - * Execute something after the current tick - * of the event loop. Override for other envs - * that have a better solution than setTimeout. - * @param {Function} fn function to execute later. - */ - req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) { - setTimeout(fn, 4); - } : function (fn) { fn(); }; - - /** - * Export require as a global, but only if it does not already exist. - */ - if (!require) { - require = req; - } - - req.version = version; - - //Used to filter out dependencies that are already paths. - req.jsExtRegExp = /^\/|:|\?|\.js$/; - req.isBrowser = isBrowser; - s = req.s = { - contexts: contexts, - newContext: newContext - }; - - //Create default context. - req({}); - - //Exports some context-sensitive methods on global require. - each([ - 'toUrl', - 'undef', - 'defined', - 'specified' - ], function (prop) { - //Reference from contexts instead of early binding to default context, - //so that during builds, the latest instance of the default context - //with its config gets used. - req[prop] = function () { - var ctx = contexts[defContextName]; - return ctx.require[prop].apply(ctx, arguments); - }; - }); - - if (isBrowser) { - head = s.head = document.getElementsByTagName('head')[0]; - //If BASE tag is in play, using appendChild is a problem for IE6. - //When that browser dies, this can be removed. Details in this jQuery bug: - //http://dev.jquery.com/ticket/2709 - baseElement = document.getElementsByTagName('base')[0]; - if (baseElement) { - head = s.head = baseElement.parentNode; - } - } - - /** - * Any errors that require explicitly generates will be passed to this - * function. Intercept/override it if you want custom error handling. - * @param {Error} err the error object. - */ - req.onError = defaultOnError; - - /** - * Creates the node for the load command. Only used in browser envs. - */ - req.createNode = function (config, moduleName, url) { - var node = config.xhtml ? - document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') : - document.createElement('script'); - node.type = config.scriptType || 'text/javascript'; - node.charset = 'utf-8'; - node.async = true; - return node; - }; - - /** - * Does the request to load a module for the browser case. - * Make this a separate function to allow other environments - * to override it. - * - * @param {Object} context the require context to find state. - * @param {String} moduleName the name of the module. - * @param {Object} url the URL to the module. - */ - req.load = function (context, moduleName, url) { - var config = (context && context.config) || {}, - node; - if (isBrowser) { - //In the browser so use a script tag - node = req.createNode(config, moduleName, url); - - node.setAttribute('data-requirecontext', context.contextName); - node.setAttribute('data-requiremodule', moduleName); - - //Set up load listener. Test attachEvent first because IE9 has - //a subtle issue in its addEventListener and script onload firings - //that do not match the behavior of all other browsers with - //addEventListener support, which fire the onload event for a - //script right after the script execution. See: - //https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution - //UNFORTUNATELY Opera implements attachEvent but does not follow the script - //script execution mode. - if (node.attachEvent && - //Check if node.attachEvent is artificially added by custom script or - //natively supported by browser - //read https://github.com/requirejs/requirejs/issues/187 - //if we can NOT find [native code] then it must NOT natively supported. - //in IE8, node.attachEvent does not have toString() - //Note the test for "[native code" with no closing brace, see: - //https://github.com/requirejs/requirejs/issues/273 - !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) && - !isOpera) { - //Probably IE. IE (at least 6-8) do not fire - //script onload right after executing the script, so - //we cannot tie the anonymous define call to a name. - //However, IE reports the script as being in 'interactive' - //readyState at the time of the define call. - useInteractive = true; - - node.attachEvent('onreadystatechange', context.onScriptLoad); - //It would be great to add an error handler here to catch - //404s in IE9+. However, onreadystatechange will fire before - //the error handler, so that does not help. If addEventListener - //is used, then IE will fire error before load, but we cannot - //use that pathway given the connect.microsoft.com issue - //mentioned above about not doing the 'script execute, - //then fire the script load event listener before execute - //next script' that other browsers do. - //Best hope: IE10 fixes the issues, - //and then destroys all installs of IE 6-9. - //node.attachEvent('onerror', context.onScriptError); - } else { - node.addEventListener('load', context.onScriptLoad, false); - node.addEventListener('error', context.onScriptError, false); - } - node.src = url; - - //Calling onNodeCreated after all properties on the node have been - //set, but before it is placed in the DOM. - if (config.onNodeCreated) { - config.onNodeCreated(node, config, moduleName, url); - } - - //For some cache cases in IE 6-8, the script executes before the end - //of the appendChild execution, so to tie an anonymous define - //call to the module name (which is stored on the node), hold on - //to a reference to this node, but clear after the DOM insertion. - currentlyAddingScript = node; - if (baseElement) { - head.insertBefore(node, baseElement); - } else { - head.appendChild(node); - } - currentlyAddingScript = null; - - return node; - } else if (isWebWorker) { - try { - //In a web worker, use importScripts. This is not a very - //efficient use of importScripts, importScripts will block until - //its script is downloaded and evaluated. However, if web workers - //are in play, the expectation is that a build has been done so - //that only one script needs to be loaded anyway. This may need - //to be reevaluated if other use cases become common. - - // Post a task to the event loop to work around a bug in WebKit - // where the worker gets garbage-collected after calling - // importScripts(): https://webkit.org/b/153317 - setTimeout(function() {}, 0); - importScripts(url); - - //Account for anonymous modules - context.completeLoad(moduleName); - } catch (e) { - context.onError(makeError('importscripts', - 'importScripts failed for ' + - moduleName + ' at ' + url, - e, - [moduleName])); - } - } - }; - - function getInteractiveScript() { - if (interactiveScript && interactiveScript.readyState === 'interactive') { - return interactiveScript; - } - - eachReverse(scripts(), function (script) { - if (script.readyState === 'interactive') { - return (interactiveScript = script); - } - }); - return interactiveScript; - } - - //Look for a data-main script attribute, which could also adjust the baseUrl. - if (isBrowser && !cfg.skipDataMain) { - //Figure out baseUrl. Get it from the script tag with require.js in it. - eachReverse(scripts(), function (script) { - //Set the 'head' where we can append children by - //using the script's parent. - if (!head) { - head = script.parentNode; - } - - //Look for a data-main attribute to set main script for the page - //to load. If it is there, the path to data main becomes the - //baseUrl, if it is not already set. - dataMain = script.getAttribute('data-main'); - if (dataMain) { - //Preserve dataMain in case it is a path (i.e. contains '?') - mainScript = dataMain; - - //Set final baseUrl if there is not already an explicit one, - //but only do so if the data-main value is not a loader plugin - //module ID. - if (!cfg.baseUrl && mainScript.indexOf('!') === -1) { - //Pull off the directory of data-main for use as the - //baseUrl. - src = mainScript.split('/'); - mainScript = src.pop(); - subPath = src.length ? src.join('/') + '/' : './'; - - cfg.baseUrl = subPath; - } - - //Strip off any trailing .js since mainScript is now - //like a module name. - mainScript = mainScript.replace(jsSuffixRegExp, ''); - - //If mainScript is still a path, fall back to dataMain - if (req.jsExtRegExp.test(mainScript)) { - mainScript = dataMain; - } - - //Put the data-main script in the files to load. - cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript]; - - return true; - } - }); - } - - /** - * The function that handles definitions of modules. Differs from - * require() in that a string for the module should be the first argument, - * and the function to execute after dependencies are loaded should - * return a value to define the module corresponding to the first argument's - * name. - */ - define = function (name, deps, callback) { - var node, context; - - //Allow for anonymous modules - if (typeof name !== 'string') { - //Adjust args appropriately - callback = deps; - deps = name; - name = null; - } - - //This module may not have dependencies - if (!isArray(deps)) { - callback = deps; - deps = null; - } - - //If no name, and callback is a function, then figure out if it a - //CommonJS thing with dependencies. - if (!deps && isFunction(callback)) { - deps = []; - //Remove comments from the callback string, - //look for require calls, and pull them into the dependencies, - //but only if there are function args. - if (callback.length) { - callback - .toString() - .replace(commentRegExp, commentReplace) - .replace(cjsRequireRegExp, function (match, dep) { - deps.push(dep); - }); - - //May be a CommonJS thing even without require calls, but still - //could use exports, and module. Avoid doing exports and module - //work though if it just needs require. - //REQUIRES the function to expect the CommonJS variables in the - //order listed below. - deps = (callback.length === 1 ? ['require'] : ['require', 'exports', 'module']).concat(deps); - } - } - - //If in IE 6-8 and hit an anonymous define() call, do the interactive - //work. - if (useInteractive) { - node = currentlyAddingScript || getInteractiveScript(); - if (node) { - if (!name) { - name = node.getAttribute('data-requiremodule'); - } - context = contexts[node.getAttribute('data-requirecontext')]; - } - } - - //Always save off evaluating the def call until the script onload handler. - //This allows multiple modules to be in a file without prematurely - //tracing dependencies, and allows for anonymous module support, - //where the module name is not known until the script onload event - //occurs. If no context, use the global queue, and get it processed - //in the onscript load callback. - if (context) { - context.defQueue.push([name, deps, callback]); - context.defQueueMap[name] = true; - } else { - globalDefQueue.push([name, deps, callback]); - } - }; - - define.amd = { - jQuery: true - }; - - /** - * Executes the text. Normally just uses eval, but can be modified - * to use a better, environment-specific call. Only used for transpiling - * loader plugins, not for plain JS modules. - * @param {String} text the text to execute/evaluate. - */ - req.exec = function (text) { - /*jslint evil: true */ - return eval(text); - }; - - //Set up with config info. - req(cfg); -}(this, (typeof setTimeout === 'undefined' ? undefined : setTimeout)));
diff --git a/packages/concatjs/web_test/BUILD.bazel b/packages/concatjs/web_test/BUILD.bazel deleted file mode 100644 index 17d5559..0000000 --- a/packages/concatjs/web_test/BUILD.bazel +++ /dev/null
@@ -1,59 +0,0 @@ -# Copyright 2019 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -load("@bazel_skylib//:bzl_library.bzl", "bzl_library") -load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary") - -package(default_visibility = ["//visibility:public"]) - -exports_files(["karma.conf.js"]) - -nodejs_binary( - name = "karma_bin", - data = [ - "//packages/concatjs:bazel_concatjs", - "@npm//jasmine-core", - "@npm//karma", - "@npm//karma-chrome-launcher", - "@npm//karma-firefox-launcher", - "@npm//karma-jasmine", - "@npm//karma-junit-reporter", - "@npm//karma-requirejs", - "@npm//karma-sourcemap-loader", - "@npm//requirejs", - ], - entry_point = "@npm//:node_modules/karma/bin/karma", -) - -bzl_library( - name = "bzl", - testonly = True, - srcs = glob(["*.bzl"]), - deps = [ - "@bazel_skylib//lib:types", - "@build_bazel_rules_nodejs//:bzl", - "@build_bazel_rules_nodejs//internal/js_library:bzl", - "@io_bazel_rules_webtesting//web", - "@rules_nodejs//nodejs:bzl", - ], -) - -filegroup( - name = "package_contents", - srcs = [ - "BUILD.bazel", - "karma.conf.js", - "karma_web_test.bzl", - ], -)
diff --git a/packages/concatjs/web_test/karma.conf.js b/packages/concatjs/web_test/karma.conf.js deleted file mode 100644 index 90a03ef..0000000 --- a/packages/concatjs/web_test/karma.conf.js +++ /dev/null
@@ -1,496 +0,0 @@ -// Karma configuration -// GENERATED BY Bazel -try { - const crypto = require('crypto'); - const fs = require('fs'); - const path = require('path'); - const child_process = require('child_process'); - const runfiles = require(process.env['BAZEL_NODE_RUNFILES_HELPER']); - - const VERBOSE_LOGS = !!process.env['VERBOSE_LOGS']; - - function log_verbose(...m) { - // This is a template file so we use __filename to output the actual filename - if (VERBOSE_LOGS) console.error(`[${path.basename(__filename)}]`, ...m); - } - - // BEGIN ENV VARS - TMPL_env_vars - // END ENV VARS - - const configPath = 'TMPL_config_file'; - - log_verbose(`running with - cwd: ${process.cwd()} - configPath: ${configPath}`); - - /** - * Helper function to find a particular namedFile - * within the webTestMetadata webTestFiles - */ - function findNamedFile(webTestMetadata, key) { - let result; - webTestMetadata['webTestFiles'].forEach(entry => { - const webTestNamedFiles = entry['namedFiles']; - if (webTestNamedFiles && webTestNamedFiles[key]) { - result = webTestNamedFiles[key]; - } - }); - return result; - } - - /** - * Helper function to extract a browser archive - * and return the path to extract executable - */ - function extractWebArchive(extractExe, archiveFile, executablePath) { - try { - // Paths are relative to the root runfiles folder - extractExe = extractExe ? path.join('..', extractExe) : extractExe; - archiveFile = path.join('..', archiveFile); - const extractedExecutablePath = path.join(process.cwd(), executablePath); - if (!extractExe) { - throw new Error('No EXTRACT_EXE found'); - } - child_process.execFileSync( - extractExe, [archiveFile, '.'], {stdio: [process.stdin, process.stdout, process.stderr]}); - log_verbose( - `Extracting web archive ${archiveFile} with ${extractExe} to ${extractedExecutablePath}`); - return extractedExecutablePath; - } catch (e) { - console.error(`Failed to extract ${archiveFile}`); - throw e; - } - } - - /** - * Check if Chrome sandboxing is supported on the current platform. - */ - function supportChromeSandboxing() { - if (process.platform === 'darwin') { - // Chrome 73+ fails to initialize the sandbox on OSX when running under Bazel. - // ``` - // ERROR [launcher]: Cannot start ChromeHeadless - // ERROR:crash_report_database_mac.mm(96)] mkdir - // /private/var/tmp/_bazel_greg/62ef096b0da251c6d093468a1efbfbd3/execroot/angular/bazel-out/darwin-fastbuild/bin/external/io_bazel_rules_webtesting/third_party/chromium/chromium.out/chrome-mac/Chromium.app/Contents/Versions/73.0.3683.0/Chromium - // Framework.framework/Versions/A/new: Permission denied (13) ERROR:file_io.cc(89)] - // ReadExactly: expected 8, observed 0 ERROR:crash_report_database_mac.mm(96)] mkdir - // /private/var/tmp/_bazel_greg/62ef096b0da251c6d093468a1efbfbd3/execroot/angular/bazel-out/darwin-fastbuild/bin/external/io_bazel_rules_webtesting/third_party/chromium/chromium.out/chrome-mac/Chromium.app/Contents/Versions/73.0.3683.0/Chromium - // Framework.framework/Versions/A/new: Permission denied (13) Chromium Helper[94642] <Error>: - // SeatbeltExecServer: Failed to initialize sandbox: -1 Operation not permitted Failed to - // initialize sandbox. [0213/201206.137114:FATAL:content_main_delegate.cc(54)] Check failed: - // false. 0 Chromium Framework 0x000000010c078bc9 ChromeMain + 43788137 1 - // Chromium Framework 0x000000010bfc0f43 ChromeMain + 43035363 - // ... - // ``` - return false; - } - - if (process.platform === 'linux') { - // Chrome on Linux uses sandboxing, which needs user namespaces to be enabled. - // This is not available on all kernels and it might be turned off even if it is available. - // Notable examples where user namespaces are not available include: - // - In Debian it is compiled-in but disabled by default. - // - The Docker daemon for Windows or OSX does not support user namespaces. - // We can detect if user namespaces are supported via - // /proc/sys/kernel/unprivileged_userns_clone. For more information see: - // https://github.com/Googlechrome/puppeteer/issues/290 - // https://superuser.com/questions/1094597/enable-user-namespaces-in-debian-kernel#1122977 - // https://github.com/karma-runner/karma-chrome-launcher/issues/158 - // https://github.com/angular/angular/pull/24906 - try { - const res = child_process.execSync('cat /proc/sys/kernel/unprivileged_userns_clone') - .toString() - .trim(); - return res === '1'; - } catch (error) { - } - return false; - } - - return true; - } - - /** - * Helper function to override base karma config values. - */ - function overrideConfigValue(conf, name, value) { - if (conf.hasOwnProperty(name)) { - console.warn( - `Your karma configuration specifies '${name}' which will be overwritten by Bazel`); - } - conf[name] = value; - } - - /** - * Helper function to override nested karma config values. - */ - function overrideNestedConfigValue(conf, name, value) { - const nameParts = name.split('.'); - const finalName = nameParts.pop(); - for (const property of nameParts) { - if (!(property in conf)) { - conf[property] = {}; - } - conf = conf[property]; - } - if (conf.hasOwnProperty(name)) { - console.warn( - `Your karma configuration specifies '${name}' which will be overwritten by Bazel`); - } - conf[finalName] = value; - } - - /** - * Helper function to merge base karma config values that are arrays. - */ - function mergeConfigArray(conf, name, values) { - if (!conf[name]) { - conf[name] = []; - } - values.forEach(v => { - if (!conf[name].includes(v)) { - conf[name].push(v); - } - }) - } - - function tryRequire(packageName) { - try { - return require(packageName); - } catch (e) { - if (e && e.code === 'MODULE_NOT_FOUND') { - return undefined; - } - - throw e; - } - } - - /** - * Configuration settings for karma under Bazel common to karma_web_test - * and karma_web_test_suite. - */ - function configureBazelConfig(config, conf) { - // list of karma plugins - mergeConfigArray(conf, 'plugins', [ - // Loads 'concat_js' - require('@bazel/concatjs'), - // Load plugins that are peer deps. These packages are used in this config file. - require('karma-chrome-launcher'), - require('karma-firefox-launcher'), - require('karma-jasmine'), - require('karma-requirejs'), - require('karma-sourcemap-loader'), - // Loads user-installed karma-* packages in the root node_modules. - 'karma-*', - ]); - - // list of karma preprocessors - if (!conf.preprocessors) { - conf.preprocessors = {} - } - conf.preprocessors['**/*.js'] = ['sourcemap']; - - // list of test frameworks to use - overrideConfigValue(conf, 'frameworks', ['jasmine', 'concat_js']); - - // test results reporter to use - // possible values: 'dots', 'progress' - // available reporters: https://npmjs.org/browse/keyword/karma-reporter - mergeConfigArray(conf, 'reporters', ['progress']); - - // enable / disable colors in the output (reporters and logs) - if (!conf.colors) { - conf.colors = true; - } - - // level of logging - // possible values: config.LOG_DISABLE || config.LOG_ERROR || - // config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG - conf.logLevel = VERBOSE_LOGS ? config.LOG_DEBUG : config.LOG_INFO; - - // enable / disable watching file and executing tests whenever - // any file changes - overrideConfigValue(conf, 'autoWatch', process.env['IBAZEL_NOTIFY_CHANGES'] === 'y'); - - // Continuous Integration mode - // if true, Karma captures browsers, runs the tests and exits - // note: run_karma.sh may override this as a command-line option. - overrideConfigValue(conf, 'singleRun', false); - - // Concurrency level - // how many browser should be started simultaneous - overrideConfigValue(conf, 'concurrency', Infinity); - - // base path that will be used to resolve all patterns - // (eg. files, exclude) - overrideConfigValue(conf, 'basePath', 'TMPL_runfiles_path'); - - // Do not show "no timestamp" errors from "karma-requirejs" for proxied file - // requests. Files which are passed as "static_files" are proxied by default and - // therefore should not cause such an exception when loaded as expected. - // See: https://github.com/karma-runner/karma-requirejs/issues/6 - const requireJsShowNoTimestampsError = '^(?!/base/).*$'; - - if (conf.client) { - overrideConfigValue( - conf.client, 'requireJsShowNoTimestampsError', requireJsShowNoTimestampsError); - } else { - conf.client = {requireJsShowNoTimestampsError}; - } - - // Enable the junit reporter if the XML_OUTPUT_FILE environment variable - // is defined and the karma-junit-reporter package is installed. - // The configuration for the junit reporter will be created or overridden - // with the configuration required for bazel to work properly. - const testOutputFile = process.env.XML_OUTPUT_FILE; - const karmaJunitReporterPlugin = testOutputFile ? tryRequire('karma-junit-reporter') : undefined; - if (karmaJunitReporterPlugin) { - mergeConfigArray(conf, 'plugins', [ - karmaJunitReporterPlugin, - ]); - - mergeConfigArray(conf, 'reporters', ['junit']); - overrideNestedConfigValue(conf, 'junitReporter.outputDir', path.dirname(testOutputFile)); - overrideNestedConfigValue(conf, 'junitReporter.outputFile', path.basename(testOutputFile)); - overrideNestedConfigValue(conf, 'junitReporter.useBrowserName', false); - } - } - - /** - * Configure the 'files' and 'proxies' configuration attributes. - * These are concatenated into a single file by karma-concat-js. - */ - function configureFiles(conf) { - overrideConfigValue(conf, 'files', []); - overrideConfigValue(conf, 'exclude', []); - overrideConfigValue(conf, 'proxies', {}); - - // Static files are added to the files array but configured to not be included, - // so karma-concat-js does not included them in the bundle. Note that we need - // to add the static files before adding the included files as we don't want - // to overwrite included files (changing from included to not included). - [ - // BEGIN STATIC FILES - TMPL_static_files - // END STATIC FILES - ].forEach((f) => { - // In Windows, the runfile will probably not be symlinked. Se we need to - // serve the real file through karma, and proxy calls to the expected file - // location in the runfiles to the real file. - const resolvedFile = runfiles.resolve(f); - conf.files.push({pattern: resolvedFile, included: false}); - // Prefixing the proxy path with '/absolute' allows karma to load local - // files. This doesn't see to be an official API. - // https://github.com/karma-runner/karma/issues/2703 - conf.proxies['/base/' + f] = '/absolute' + resolvedFile; - }); - - [ - // BEGIN BOOTSTRAP FILES - TMPL_bootstrap_files - // END BOOTSTRAP FILES - // BEGIN USER FILES - TMPL_user_files - // END USER FILES - ].forEach(f => conf.files.push(runfiles.resolve(f))) - - var requireConfigContent = ` - // A simplified version of Karma's requirejs.config.tpl.js for use with Karma under Bazel. - // This does an explicit \`require\` on each test script in the files, otherwise nothing will be loaded. - (function(){ - var runtimeFiles = [ - // BEGIN RUNTIME FILES - TMPL_runtime_files - // END RUNTIME FILES - ].map(function(file) { return file.replace(/\\.js$/, ''); }); - var allFiles = [ - // BEGIN USER FILES - TMPL_user_files - // END USER FILES - ]; - var allTestFiles = []; - allFiles.forEach(function (file) { - if (/[^a-zA-Z0-9](spec|test)\\.js$/i.test(file) && !/\\/node_modules\\//.test(file)) { - allTestFiles.push(file.replace(/\\.js$/, '')) - } - }); - require(runtimeFiles, function() { return require(allTestFiles, window.__karma__.start); }); - })(); - `; - - const requireConfigFile = path.join( - process.env['TEST_TMPDIR'], crypto.randomBytes(6).readUIntLE(0, 6).toString(36) + '.js'); - fs.writeFileSync(requireConfigFile, requireConfigContent); - conf.files.push(requireConfigFile); - } - - /** - * Configure karma under karma_web_test_suite. - * `browsers` and `customLaunchers` are setup by Bazel. - */ - function configureTsWebTestSuiteConfig(conf) { - // WEB_TEST_METADATA is configured in rules_webtesting based on value - // of the browsers attribute passed to karms_web_test_suite - // We setup the karma configuration based on the values in this object - if (!process.env['WEB_TEST_METADATA']) { - // This is a karma_web_test rule since there is no WEB_TEST_METADATA - return; - } - - overrideConfigValue(conf, 'browsers', []); - overrideConfigValue(conf, 'customLaunchers', null); - - const webTestMetadata = require(runfiles.resolve(process.env['WEB_TEST_METADATA'])); - log_verbose(`WEB_TEST_METADATA: ${JSON.stringify(webTestMetadata, null, 2)}`); - if (webTestMetadata['environment'] === 'local') { - // When a local chrome or firefox browser is chosen such as - // "@io_bazel_rules_webtesting//browsers:chromium-local" or - // "@io_bazel_rules_webtesting//browsers:firefox-local" - // then the 'environment' will equal 'local' and - // 'webTestFiles' will contain the path to the binary to use - const extractExe = findNamedFile(webTestMetadata, 'EXTRACT_EXE'); - webTestMetadata['webTestFiles'].forEach(webTestFiles => { - const webTestNamedFiles = webTestFiles['namedFiles']; - const archiveFile = webTestFiles['archiveFile']; - if (webTestNamedFiles['CHROMIUM']) { - // When karma is configured to use Chrome it will look for a CHROME_BIN - // environment variable. - if (archiveFile) { - process.env.CHROME_BIN = - extractWebArchive(extractExe, archiveFile, webTestNamedFiles['CHROMIUM']); - } else { - try { - process.env.CHROME_BIN = runfiles.resolve(webTestNamedFiles['CHROMIUM']); - } catch { - // Fail as this file is expected to be in runfiles - throw new Error(`Failed to resolve rules_webtesting Chromium binary '${ - webTestNamedFiles['CHROMIUM']}' in runfiles`); - } - } - // Read any additional chrome options (as specified by the - // rules_webtesting manifest). - const chromeOptions = (webTestMetadata['capabilities'] || {})['goog:chromeOptions']; - const additionalArgs = (chromeOptions ? chromeOptions['args'] : []).filter(arg => { - // We never want to 'run' Chrome in headless mode. - return arg != '--headless'; - }); - const browser = process.env['DISPLAY'] ? 'Chrome' : 'ChromeHeadless'; - if (!supportChromeSandboxing()) { - const launcher = 'CustomChromeWithoutSandbox'; - conf.customLaunchers = - {[launcher]: {base: browser, flags: ['--no-sandbox', ...additionalArgs]}}; - conf.browsers.push(launcher); - } else { - const launcher = 'CustomChrome'; - conf.customLaunchers = {[launcher]: {base: browser, flags: additionalArgs}}; - conf.browsers.push(launcher); - } - } - if (webTestNamedFiles['FIREFOX']) { - // When karma is configured to use Firefox it will look for a - // FIREFOX_BIN environment variable. - if (archiveFile) { - process.env.FIREFOX_BIN = - extractWebArchive(extractExe, archiveFile, webTestNamedFiles['FIREFOX']); - } else { - try { - process.env.FIREFOX_BIN = runfiles.resolve(webTestNamedFiles['FIREFOX']); - } catch { - // Fail as this file is expected to be in runfiles - throw new Error(`Failed to resolve rules_webtesting Firefox binary '${ - webTestNamedFiles['FIREFOX']}' in runfiles`); - } - } - - // For Firefox, we need to disable the content sandbox as the browser is already being - // launched as part of the Bazel sandbox. This is necessary because the integrated content - // sandbox in Firefox will conflict with the Bazel sandbox due to nested sandboxing. - // This is similar to why we disable sandbox for Chromium (as seen above). - process.env.MOZ_DISABLE_CONTENT_SANDBOX = '1'; - - conf.browsers.push(process.env['DISPLAY'] ? 'Firefox' : 'FirefoxHeadless'); - } - }); - } else { - throw new Error(`Unknown WEB_TEST_METADATA environment '${webTestMetadata['environment']}'`); - } - - if (!conf.browsers.length) { - throw new Error('No browsers configured in web test suite'); - } - } - - function configureTsWebTestConfig(conf) { - if (process.env['WEB_TEST_METADATA']) { - // This is a karma_web_test_suite rule since there is a WEB_TEST_METADATA - return; - } - - // Fallback to using the system local chrome if no valid browsers have been - // configured above - if (!conf.browsers || !conf.browsers.length) { - console.warn('No browsers configured. Configuring Karma to use system Chrome.'); - conf.browsers = [process.env['DISPLAY'] ? 'Chrome' : 'ChromeHeadless']; - } - } - - function configureFormatError(conf) { - conf.formatError = (msg) => { - // This is a bazel specific formatError that removes the workspace - // name from stack traces. - // Look for filenames of the format "(<filename>:<row>:<column>" - const FILENAME_REGEX = /\(([^:\n\r]+)(:\d+:\d+)/gm; - msg = msg.replace(FILENAME_REGEX, (_, p1, p2) => { - if (p1.startsWith('../')) { - // Remove all leading "../" - while (p1.startsWith('../')) { - p1 = p1.substr(3); - } - } else { - // Remove workspace name(angular, ngdeps etc.) from the beginning. - const index = p1.indexOf('/'); - if (index >= 0) { - p1 = p1.substr(index + 1); - } - } - return '(' + p1 + p2; - }); - return msg + '\n\n'; - }; - } - - module.exports = function(config) { - let conf = {}; - - // Import the user's base karma configuration if specified - if (configPath) { - const baseConf = require(runfiles.resolve(configPath)); - if (typeof baseConf !== 'function') { - throw new Error( - 'Invalid base karma configuration. Expected config function to be exported.'); - } - const originalSetConfig = config.set; - config.set = function(c) { - conf = c; - }; - baseConf(config); - config.set = originalSetConfig; - log_verbose(`base karma configuration: ${JSON.stringify(conf, null, 2)}`); - } - - configureBazelConfig(config, conf); - configureFiles(conf); - configureTsWebTestSuiteConfig(conf); - configureTsWebTestConfig(conf); - configureFormatError(conf); - - log_verbose(`karma configuration: ${JSON.stringify(conf, null, 2)}`); - - config.set(conf); - } -} catch (e) { - console.error('Error in karma configuration', e.toString()); - throw e; -}
diff --git a/packages/concatjs/web_test/karma_web_test.bzl b/packages/concatjs/web_test/karma_web_test.bzl deleted file mode 100644 index f45aced..0000000 --- a/packages/concatjs/web_test/karma_web_test.bzl +++ /dev/null
@@ -1,487 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"Unit testing with Karma" - -load("@rules_nodejs//nodejs:providers.bzl", "JSModuleInfo") -load("@build_bazel_rules_nodejs//:providers.bzl", "ExternalNpmPackageInfo", "JSNamedModuleInfo", "node_modules_aspect") -load("@build_bazel_rules_nodejs//internal/js_library:js_library.bzl", "write_amd_names_shim") -load("@io_bazel_rules_webtesting//web:web.bzl", "web_test_suite") -load("@io_bazel_rules_webtesting//web/internal:constants.bzl", "DEFAULT_TEST_SUITE_TAGS", "DEFAULT_WRAPPED_TEST_TAGS") - -KARMA_PEER_DEPS = [ - # NB: uncommented during pkg_npm - #@external "@npm//@bazel/concatjs", - "@npm//karma", - "@npm//karma-chrome-launcher", - "@npm//karma-firefox-launcher", - "@npm//karma-jasmine", - "@npm//karma-requirejs", - "@npm//karma-sourcemap-loader", - "@npm//requirejs", -] - -KARMA_WEB_TEST_ATTRS = { - "bootstrap": attr.label_list( - doc = """JavaScript files to include *before* the module loader (require.js). - For example, you can include Reflect,js for TypeScript decorator metadata reflection, - or UMD bundles for third-party libraries.""", - allow_files = [".js"], - ), - "config_file": attr.label( - doc = """User supplied Karma configuration file. Bazel will override - certain attributes of this configuration file. Attributes that are - overridden will be outputted to the test log.""", - allow_single_file = True, - ), - "configuration_env_vars": attr.string_list( - doc = """Pass these configuration environment variables to the resulting binary. - Chooses a subset of the configuration environment variables (taken from ctx.var), which also - includes anything specified via the --define flag. - Note, this can lead to different outputs produced by this rule.""", - default = [], - ), - "data": attr.label_list( - doc = "Runtime dependencies", - allow_files = True, - ), - "deps": attr.label_list( - doc = "Other targets which produce JavaScript such as `ts_library`", - allow_files = True, - aspects = [node_modules_aspect], - ), - "karma": attr.label( - doc = "karma binary label", - # NB: replaced during pkg_npm with "@npm//karma/bin:karma" - default = "//packages/concatjs/web_test:karma_bin", - executable = True, - cfg = "exec", - allow_files = True, - ), - "runtime_deps": attr.label_list( - doc = """Dependencies which should be loaded after the module loader but before the srcs and deps. - These should be a list of targets which produce JavaScript such as `ts_library`. - The files will be loaded in the same order they are declared by that rule.""", - allow_files = True, - aspects = [node_modules_aspect], - ), - "srcs": attr.label_list( - doc = "A list of JavaScript test files", - allow_files = [".js"], - ), - "static_files": attr.label_list( - doc = """Arbitrary files which are available to be served on request. - Files are served at: - `/base/<WORKSPACE_NAME>/<path-to-file>`, e.g. - `/base/npm_bazel_typescript/examples/testing/static_script.js`""", - allow_files = True, - ), - "_conf_tmpl": attr.label( - default = "//packages/concatjs/web_test:karma.conf.js", - allow_single_file = True, - ), -} - -# Avoid using non-normalized paths (workspace/../other_workspace/path) -def _to_manifest_path(ctx, file): - if file.short_path.startswith("../"): - return file.short_path[3:] - else: - return ctx.workspace_name + "/" + file.short_path - -# Write the AMD names shim bootstrap file -def _write_amd_names_shim(ctx): - amd_names_shim = ctx.actions.declare_file( - "_%s.amd_names_shim.js" % ctx.label.name, - sibling = ctx.outputs.executable, - ) - write_amd_names_shim(ctx.actions, amd_names_shim, ctx.attr.bootstrap) - return amd_names_shim - -def _filter_js(files): - return [f for f in files if f.extension == "js" or f.extension == "mjs"] - -def _find_dep(ctx, suffix): - for d in ctx.files.deps: - if (d.path.endswith(suffix)): - return _to_manifest_path(ctx, d) - fail("couldn't find file %s in the deps" % suffix) - -# Generates the karma configuration file for the rule -def _write_karma_config(ctx, files, amd_names_shim): - configuration = ctx.outputs.configuration - - config_file = None - - if ctx.attr.config_file: - if JSModuleInfo in ctx.attr.config_file: - config_file = _filter_js(ctx.attr.config_file[JSModuleInfo].direct_sources.to_list())[0] - else: - config_file = ctx.file.config_file - - # The files in the bootstrap attribute come before the require.js support. - # Note that due to frameworks = ['jasmine'], a few scripts will come before - # the bootstrap entries: - # karma-jasmine/lib/boot.js - # karma-jasmine/lib/adapter.js - # This is desired so that the bootstrap entries can patch jasmine, as zone.js does. - bootstrap_entries = [ - _to_manifest_path(ctx, f) - for f in ctx.files.bootstrap - ] - - # Explicitly list the requirejs library files here, rather than use - # `frameworks: ['requirejs']` - # so that we control the script order, and the bootstrap files come before - # require.js. - # That allows bootstrap files to have anonymous AMD modules, or to do some - # polyfilling before test libraries load. - # See https://github.com/karma-runner/karma/issues/699 - bootstrap_entries += [ - _find_dep(ctx, "requirejs/require.js"), - _find_dep(ctx, "karma-requirejs/lib/adapter.js"), - "/".join([ctx.workspace_name, amd_names_shim.short_path]), - ] - - # Next we load the "runtime_deps" which we expect to contain named AMD modules - # Thus they should come after the require.js script, but before any srcs or deps - runtime_files = [] - for dep in ctx.attr.runtime_deps: - if JSNamedModuleInfo in dep: - for src in dep[JSNamedModuleInfo].direct_sources.to_list(): - runtime_files.append(_to_manifest_path(ctx, src)) - if not JSNamedModuleInfo in dep and not ExternalNpmPackageInfo in dep and hasattr(dep, "files"): - # These are javascript files provided by DefaultInfo from a direct - # dep that has no JSNamedModuleInfo provider or ExternalNpmPackageInfo - # provider (not an npm dep). These files must be in named AMD or named - # UMD format. - for src in dep.files.to_list(): - runtime_files.append(_to_manifest_path(ctx, src)) - - # Finally we load the user's srcs and deps - user_entries = [ - _to_manifest_path(ctx, f) - for f in files.to_list() - if f.path.endswith(".js") - ] - - # Expand static_files paths to runfiles for config - static_files = [ - _to_manifest_path(ctx, f) - for f in ctx.files.static_files - ] - - # root-relative (runfiles) path to the directory containing karma.conf - config_segments = len(configuration.short_path.split("/")) - - # configuration_env_vars are set using process.env() - env_vars = "" - for k in ctx.attr.configuration_env_vars: - if k in ctx.var.keys(): - env_vars += "process.env[\"%s\"]=\"%s\";\n" % (k, ctx.var[k]) - - ctx.actions.expand_template( - output = configuration, - template = ctx.file._conf_tmpl, - substitutions = { - "TMPL_bootstrap_files": "\n ".join(["'%s'," % e for e in bootstrap_entries]), - "TMPL_config_file": _to_manifest_path(ctx, config_file) if config_file else "", - "TMPL_env_vars": env_vars, - "TMPL_runfiles_path": "/".join([".."] * config_segments), - "TMPL_runtime_files": "\n ".join(["'%s'," % e for e in runtime_files]), - "TMPL_static_files": "\n ".join(["'%s'," % e for e in static_files]), - "TMPL_user_files": "\n ".join(["'%s'," % e for e in user_entries]), - }, - ) - - return configuration - -def _karma_web_test_impl(ctx): - files_depsets = [depset(ctx.files.srcs)] - for dep in ctx.attr.deps + ctx.attr.runtime_deps: - if JSNamedModuleInfo in dep: - files_depsets.append(dep[JSNamedModuleInfo].sources) - if not JSNamedModuleInfo in dep and not ExternalNpmPackageInfo in dep and hasattr(dep, "files"): - # These are javascript files provided by DefaultInfo from a direct - # dep that has no JSNamedModuleInfo provider or ExternalNpmPackageInfo - # provider (not an npm dep). These files must be in named AMD or named - # UMD format. - files_depsets.append(dep.files) - files = depset(transitive = files_depsets) - - # Also include files from npm fine grained deps as inputs. - # These deps are identified by the ExternalNpmPackageInfo provider. - node_modules_depsets = [] - for dep in ctx.attr.deps + ctx.attr.runtime_deps: - if ExternalNpmPackageInfo in dep: - node_modules_depsets.append(dep[ExternalNpmPackageInfo].sources) - node_modules = depset(transitive = node_modules_depsets) - - amd_names_shim = _write_amd_names_shim(ctx) - - configuration = _write_karma_config(ctx, files, amd_names_shim) - - ctx.actions.write( - output = ctx.outputs.executable, - is_executable = True, - content = """#!/usr/bin/env bash -# --- begin runfiles.bash initialization v2 --- -# Copy-pasted from the Bazel Bash runfiles library v2. -set -uo pipefail; f=build_bazel_rules_nodejs/third_party/github.com/bazelbuild/bazel/tools/bash/runfiles/runfiles.bash -source "${{RUNFILES_DIR:-/dev/null}}/$f" 2>/dev/null || \ - source "$(grep -sm1 "^$f " "${{RUNFILES_MANIFEST_FILE:-/dev/null}}" | cut -f2- -d' ')" 2>/dev/null || \ - source "$0.runfiles/$f" 2>/dev/null || \ - source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ - source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ - {{ echo>&2 "ERROR: cannot find $f"; exit 1; }}; f=; set -e -# --- end runfiles.bash initialization v2 --- - -readonly KARMA=$(rlocation "{TMPL_karma}") -readonly CONF=$(rlocation "{TMPL_conf}") - -export HOME=$(mktemp -d) - -ARGV=( "start" ${{CONF}} ) - -# Detect that we are running as a test, by using well-known environment -# variables. See go/test-encyclopedia -# Note: in Bazel 0.14 and later, TEST_TMPDIR is set for both bazel test and bazel run -# so we also check for the BUILD_WORKSPACE_DIRECTORY which is set only for bazel run -if [[ ! -z "${{TEST_TMPDIR:-}}" && ! -n "${{BUILD_WORKSPACE_DIRECTORY:-}}" ]]; then - ARGV+=( "--single-run" ) -fi - -# Pass --node_options from args on karma node process -NODE_OPTIONS=() -for ARG in "$@"; do - case "${{ARG}}" in - --node_options=*) NODE_OPTIONS+=( "${{ARG}}" ) ;; - esac -done - -KARMA_VERSION=$(${{KARMA}} --version) - -printf "\n\n\n\nRunning karma tests\n-----------------------------------------------------------------------------\n" -echo "version :" ${{KARMA_VERSION#Karma version: }} -echo "pwd :" ${{PWD}} -echo "conf :" ${{CONF}} -echo "node_options:" ${{NODE_OPTIONS[@]:-}} -printf "\n" - -readonly COMMAND="${{KARMA}} ${{ARGV[@]}} ${{NODE_OPTIONS[@]:-}}" -${{COMMAND}} -""".format( - TMPL_karma = _to_manifest_path(ctx, ctx.executable.karma), - TMPL_conf = _to_manifest_path(ctx, configuration), - ), - ) - - config_sources = [] - - if ctx.attr.config_file: - if JSModuleInfo in ctx.attr.config_file: - config_sources = ctx.attr.config_file[JSModuleInfo].sources.to_list() - else: - config_sources = [ctx.file.config_file] - - runfiles = [ - configuration, - amd_names_shim, - ] - runfiles += config_sources - runfiles += ctx.files.srcs - runfiles += ctx.files.deps - runfiles += ctx.files.runtime_deps - runfiles += ctx.files.bootstrap - runfiles += ctx.files.static_files - runfiles += ctx.files.data - - return [DefaultInfo( - files = depset([ctx.outputs.executable]), - runfiles = ctx.runfiles( - files = runfiles, - transitive_files = depset(transitive = [files, node_modules]), - ).merge(ctx.attr.karma[DefaultInfo].data_runfiles), - executable = ctx.outputs.executable, - )] - -_karma_web_test = rule( - implementation = _karma_web_test_impl, - test = True, - executable = True, - attrs = KARMA_WEB_TEST_ATTRS, - outputs = { - "configuration": "%{name}.conf.js", - }, -) - -def karma_web_test( - srcs = [], - deps = [], - data = [], - configuration_env_vars = [], - bootstrap = [], - runtime_deps = [], - static_files = [], - config_file = None, - tags = [], - peer_deps = KARMA_PEER_DEPS, - **kwargs): - """Runs unit tests in a browser with Karma. - - When executed under `bazel test`, this uses a headless browser for speed. - This is also because `bazel test` allows multiple targets to be tested together, - and we don't want to open a Chrome window on your machine for each one. Also, - under `bazel test` the test will execute and immediately terminate. - - Running under `ibazel test` gives you a "watch mode" for your tests. The rule is - optimized for this case - the test runner server will stay running and just - re-serve the up-to-date JavaScript source bundle. - - To debug a single test target, run it with `bazel run` instead. This will open a - browser window on your computer. Also you can use any other browser by opening - the URL printed when the test starts up. The test will remain running until you - cancel the `bazel run` command. - - This rule will use your system Chrome by default. In the default case, your - environment must specify CHROME_BIN so that the rule will know which Chrome binary to run. - Other `browsers` and `customLaunchers` may be set using the a base Karma configuration - specified in the `config_file` attribute. - - By default we open a headless Chrome. To use a real Chrome browser window, you can pass - `--define DISPLAY=true` to Bazel, along with `configuration_env_vars = ["DISPLAY"]` on - `karma_web_test`. - - Args: - srcs: A list of JavaScript test files - deps: Other targets which produce JavaScript such as `ts_library` - data: Runtime dependencies - configuration_env_vars: Pass these configuration environment variables to the resulting binary. - Chooses a subset of the configuration environment variables (taken from ctx.var), which also - includes anything specified via the --define flag. - Note, this can lead to different outputs produced by this rule. - bootstrap: JavaScript files to include *before* the module loader (require.js). - For example, you can include Reflect,js for TypeScript decorator metadata reflection, - or UMD bundles for third-party libraries. - runtime_deps: Dependencies which should be loaded after the module loader but before the srcs and deps. - These should be a list of targets which produce JavaScript such as `ts_library`. - The files will be loaded in the same order they are declared by that rule. - static_files: Arbitrary files which are available to be served on request. - Files are served at: - `/base/<WORKSPACE_NAME>/<path-to-file>`, e.g. - `/base/npm_bazel_typescript/examples/testing/static_script.js` - config_file: User supplied Karma configuration file. Bazel will override - certain attributes of this configuration file. Attributes that are - overridden will be outputted to the test log. - tags: Standard Bazel tags, this macro adds tags for ibazel support - peer_deps: list of peer npm deps required by karma_web_test - **kwargs: Passed through to `karma_web_test` - """ - - _karma_web_test( - srcs = srcs, - deps = deps + peer_deps, - data = data, - configuration_env_vars = configuration_env_vars, - bootstrap = bootstrap, - runtime_deps = runtime_deps, - static_files = static_files, - config_file = config_file, - tags = tags + [ - # Users don't need to know that this tag is required to run under ibazel - "ibazel_notify_changes", - ], - **kwargs - ) - -def karma_web_test_suite( - name, - browsers = None, - web_test_data = [], - wrapped_test_tags = list(DEFAULT_WRAPPED_TEST_TAGS), - **kwargs): - """Defines a test_suite of web_test targets that wrap a karma_web_test target. - - This macro accepts all parameters in karma_web_test and adds additional parameters - for the suite. See karma_web_test docs for all karma_web_test. - - The wrapping macro is `web_test_suite` which comes from rules_websting: - https://github.com/bazelbuild/rules_webtesting/blob/master/web/web.bzl. - - Args: - name: The base name of the test - browsers: A sequence of labels specifying the browsers to use. - web_test_data: Data dependencies for the wrapper web_test targets. - wrapped_test_tags: A list of test tag strings to use for the wrapped - karma_web_test target. - **kwargs: Arguments for the wrapped karma_web_test target. - """ - - # Common attributes - args = kwargs.pop("args", None) - flaky = kwargs.pop("flaky", None) - local = kwargs.pop("local", None) - shard_count = kwargs.pop("shard_count", None) - size = kwargs.pop("size", "large") - timeout = kwargs.pop("timeout", None) - - # Wrapper attributes - browser_overrides = kwargs.pop("browser_overrides", None) - config = kwargs.pop("config", None) - test_suite_tags = kwargs.pop("test_suite_tags", list(DEFAULT_TEST_SUITE_TAGS)) - visibility = kwargs.pop("visibility", None) - tags = kwargs.pop("tags", []) + [ - # Users don't need to know that this tag is required to run under ibazel - "ibazel_notify_changes", - ] - if browsers == None: - browsers = ["@io_bazel_rules_webtesting//browsers:chromium-local"] - - # rules_webtesting requires the "native" tag for browsers - if not "native" in tags: - tags = tags + ["native"] - - # The wrapped `karma_web_test` target - wrapped_test_name = name + "_wrapped_test" - karma_web_test( - name = wrapped_test_name, - args = args, - flaky = flaky, - local = local, - shard_count = shard_count, - size = size, - timeout = timeout, - tags = wrapped_test_tags, - visibility = ["//visibility:private"], - **kwargs - ) - - # The wrapper `web_test_suite` target - web_test_suite( - name = name, - args = args, - flaky = flaky, - local = local, - shard_count = shard_count, - size = size, - timeout = timeout, - launcher = ":" + wrapped_test_name, - browsers = browsers, - browser_overrides = browser_overrides, - config = config, - data = web_test_data, - tags = tags, - test = wrapped_test_name, - test_suite_tags = test_suite_tags, - visibility = visibility, - )
diff --git a/packages/concatjs/web_test/test/karma/BUILD.bazel b/packages/concatjs/web_test/test/karma/BUILD.bazel deleted file mode 100644 index 148f35b..0000000 --- a/packages/concatjs/web_test/test/karma/BUILD.bazel +++ /dev/null
@@ -1,80 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -load("@build_bazel_rules_nodejs//:index.bzl", "generated_file_test") -load("@io_bazel_rules_webtesting//web:web.bzl", "custom_browser") -load("//packages/concatjs:index.bzl", "karma_web_test_suite") - -karma_web_test_suite( - name = "testing", - srcs = glob( - ["*.js"], - exclude = [ - "coverage_source_uncovered.js", - "coverage_source.js", - "coverage.spec.js", - "unnamed-amd-module.js", - "init-test.js", - ], - ), - browsers = [ - "@io_bazel_rules_webtesting//browsers:chromium-local", - "@io_bazel_rules_webtesting//browsers:firefox-local", - ":custom_chrome", - ], - static_files = [ - "unnamed-amd-module.js", - # Add the "init-test.js" file to the "static_files" as well. This simulates - # a scenario where a developer has a NPM package in the "deps" that transitively - # brings in "tslib", whereas the target in the "static_files" can also bring - # in "tslib" transitively. - "init-test.js", - ], - tags = [ - "native", - "no-rbe", - ], - deps = [ - "init-test.js", - "requirejs-config.js", - ], -) - -karma_web_test_suite( - name = "coverage_test", - srcs = [ - "coverage.spec.js", - "coverage_source.js", - "coverage_source_uncovered.js", - ], - browsers = [ - "@io_bazel_rules_webtesting//browsers:chromium-local", - "@io_bazel_rules_webtesting//browsers:firefox-local", - ], - tags = ["native"], -) - -custom_browser( - name = "custom_chrome", - browser = "@io_bazel_rules_webtesting//browsers:chromium-local", - metadata = "custom_chrome.json", - tags = ["no-rbe"], -) - -generated_file_test( - name = "test_custom_chrome_karma_conf", - src = "karma.conf.js.golden", - generated = "testing_wrapped_test.conf.js", - substring_search = True, -)
diff --git a/packages/concatjs/web_test/test/karma/amd-modules.spec.js b/packages/concatjs/web_test/test/karma/amd-modules.spec.js deleted file mode 100644 index d971f92..0000000 --- a/packages/concatjs/web_test/test/karma/amd-modules.spec.js +++ /dev/null
@@ -1,32 +0,0 @@ -define( - 'build_bazel_rules_nodejs/packages/concatjs/web_test/test/karma/amd-modules.spec', ['require'], require => { - describe('AMD module loading', () => { - describe('unnamed amd modules', () => { - it('should not warn if module is configured as static file', doneFn => { - spyOn(console, 'error'); - - require(['unnamed-module'], () => { - // Loading such an anonymous AMD module should not cause any error messages about - // a missing timestamp. This is a limitation of the "karma-requirejs" plugin which - // by default always prints an error for requests through Karma's proxy. - // See: https://github.com/karma-runner/karma-requirejs/issues/6 - expect(console.error).toHaveBeenCalledTimes(0); - doneFn(); - }); - }); - - it('should warn if module is not specified as static file', doneFn => { - spyOn(console, 'error').and.callThrough(); - - require( - ['unnamed-module-invalid-file'], - /* loaded callback */ () => {}, - /* error callback */ () => { - expect(console.error) - .toHaveBeenCalledWith(jasmine.stringMatching(/no timestamp/)); - doneFn(); - }); - }); - }); - }); - });
diff --git a/packages/concatjs/web_test/test/karma/coverage.spec.js b/packages/concatjs/web_test/test/karma/coverage.spec.js deleted file mode 100644 index 4486f0d..0000000 --- a/packages/concatjs/web_test/test/karma/coverage.spec.js +++ /dev/null
@@ -1,27 +0,0 @@ -(function(factory) { -if (typeof module === 'object' && typeof module.exports === 'object') { - var v = factory(require, exports); - if (v !== undefined) module.exports = v; -} else if (typeof define === 'function' && define.amd) { - define( - 'build_bazel_rules_nodejs/packages/concatjs/web_test/test/karma/coverage.spec', - [ - 'require', 'exports', - 'build_bazel_rules_nodejs/packages/concatjs/web_test/test/karma/coverage_source' - ], - factory); -} -})(function(require, exports) { -'use strict'; -Object.defineProperty(exports, '__esModule', {value: true}); -var coverage_source_1 = - require('build_bazel_rules_nodejs/packages/concatjs/web_test/test/karma/coverage_source'); -describe('coverage function', () => { - it('should cover one branch', () => { - expect(coverage_source_1.isString(2)).toBe(false); - }); - it('should cover the other branch', () => { - expect(coverage_source_1.isString('some string')).toBe(true); - }); -}); -});
diff --git a/packages/concatjs/web_test/test/karma/coverage_source.js b/packages/concatjs/web_test/test/karma/coverage_source.js deleted file mode 100644 index a1d2182..0000000 --- a/packages/concatjs/web_test/test/karma/coverage_source.js +++ /dev/null
@@ -1,21 +0,0 @@ -(function(factory) { -if (typeof module === 'object' && typeof module.exports === 'object') { - var v = factory(require, exports); - if (v !== undefined) module.exports = v; -} else if (typeof define === 'function' && define.amd) { - define( - 'build_bazel_rules_nodejs/packages/concatjs/web_test/test/karma/coverage_source', - ['require', 'exports'], factory); -} -})(function(require, exports) { -'use strict'; -Object.defineProperty(exports, '__esModule', {value: true}); -function isString(input) { - if (typeof input === 'string') { - return true; - } else { - return false; - } -} -exports.isString = isString; -}); \ No newline at end of file
diff --git a/packages/concatjs/web_test/test/karma/coverage_source_uncovered.js b/packages/concatjs/web_test/test/karma/coverage_source_uncovered.js deleted file mode 100644 index 8ad07de..0000000 --- a/packages/concatjs/web_test/test/karma/coverage_source_uncovered.js +++ /dev/null
@@ -1,20 +0,0 @@ -(function(factory) { -if (typeof module === 'object' && typeof module.exports === 'object') { - var v = factory(require, exports); - if (v !== undefined) module.exports = v; -} else if (typeof define === 'function' && define.amd) { - define( - 'build_bazel_rules_nodejs/packages/concatjs/web_test/test/karma/coverage_source_uncovered', - ['require', 'exports'], factory); -} -})(function(require, exports) { -'use strict'; -Object.defineProperty(exports, '__esModule', {value: true}); -// noting in this file should be required, so we can test the c8 feature all: true -// which will pick up files that aren't directly referenced by test files -// but are added to coverage as empty coverage -function notCalled(input) { - return input * 13; -} -exports.notCalled = notCalled; -}); \ No newline at end of file
diff --git a/packages/concatjs/web_test/test/karma/custom_chrome.json b/packages/concatjs/web_test/test/karma/custom_chrome.json deleted file mode 100644 index c7798d0..0000000 --- a/packages/concatjs/web_test/test/karma/custom_chrome.json +++ /dev/null
@@ -1,11 +0,0 @@ -{ - "capabilities": { - "goog:chromeOptions": { - "args": [ - "--remote-debugging-port=9222", - "--headless", - "--use-gl=swiftshader-webgl" - ] - } - } -}
diff --git a/packages/concatjs/web_test/test/karma/decrement.js b/packages/concatjs/web_test/test/karma/decrement.js deleted file mode 100644 index 9b72703..0000000 --- a/packages/concatjs/web_test/test/karma/decrement.js +++ /dev/null
@@ -1,17 +0,0 @@ -(function(factory) { -if (typeof module === 'object' && typeof module.exports === 'object') { - var v = factory(require, exports); - if (v !== undefined) module.exports = v; -} else if (typeof define === 'function' && define.amd) { - define( - 'build_bazel_rules_nodejs/packages/concatjs/web_test/test/karma/decrement', ['require', 'exports'], - factory); -} -})(function(require, exports) { -'use strict'; -Object.defineProperty(exports, '__esModule', {value: true}); -function decrement(n) { - return n - 1; -} -exports.decrement = decrement; -});
diff --git a/packages/concatjs/web_test/test/karma/decrement.spec.js b/packages/concatjs/web_test/test/karma/decrement.spec.js deleted file mode 100644 index afddf49..0000000 --- a/packages/concatjs/web_test/test/karma/decrement.spec.js +++ /dev/null
@@ -1,20 +0,0 @@ -(function(factory) { -if (typeof module === 'object' && typeof module.exports === 'object') { - var v = factory(require, exports); - if (v !== undefined) module.exports = v; -} else if (typeof define === 'function' && define.amd) { - define( - 'build_bazel_rules_nodejs/packages/concatjs/web_test/test/karma/decrement.spec', - ['require', 'exports', 'build_bazel_rules_nodejs/packages/concatjs/web_test/test/karma/decrement'], - factory); -} -})(function(require, exports) { -'use strict'; -Object.defineProperty(exports, '__esModule', {value: true}); -var decrement_1 = require('build_bazel_rules_nodejs/packages/concatjs/web_test/test/karma/decrement'); -describe('decrementing', function() { - it('should do that', function() { - expect(decrement_1.decrement(1)).toBe(0); - }); -}); -});
diff --git a/packages/concatjs/web_test/test/karma/init-test.js b/packages/concatjs/web_test/test/karma/init-test.js deleted file mode 100644 index bb6b253..0000000 --- a/packages/concatjs/web_test/test/karma/init-test.js +++ /dev/null
@@ -1,8 +0,0 @@ -/** - * File that should mimic a file that is required for the tests to run. This file is used - * to ensure that the Karma bazel rules properly include the file in the ConcatJS bundle. - */ - -define('build_bazel_rules_nodejs/packages/concatjs/web_test/test/karma/init-test', [], () => { - window['__testInitialized'] = true; -});
diff --git a/packages/concatjs/web_test/test/karma/karma.conf.js.golden b/packages/concatjs/web_test/test/karma/karma.conf.js.golden deleted file mode 100644 index c3bb4a92..0000000 --- a/packages/concatjs/web_test/test/karma/karma.conf.js.golden +++ /dev/null
@@ -1,5 +0,0 @@ -const chromeOptions = (webTestMetadata['capabilities'] || {})['goog:chromeOptions']; -const additionalArgs = (chromeOptions ? chromeOptions['args'] : []).filter(arg => { - // We never want to 'run' Chrome in headless mode. - return arg != '--headless'; -});
diff --git a/packages/concatjs/web_test/test/karma/requirejs-config.js b/packages/concatjs/web_test/test/karma/requirejs-config.js deleted file mode 100644 index e83fa1b..0000000 --- a/packages/concatjs/web_test/test/karma/requirejs-config.js +++ /dev/null
@@ -1,8 +0,0 @@ -require.config({ - paths: { - // Configure some fake AMD module that exists and should not cause a loading - // error message from the "karma-requirejs" plugin which is enabled by default. - 'unnamed-module': '/base/build_bazel_rules_nodejs/packages/concatjs/web_test/test/karma/unnamed-amd-module', - 'unnamed-module-invalid-file': '/some-invalid-file-path', - } -});
diff --git a/packages/concatjs/web_test/test/karma/test-initialized.spec.js b/packages/concatjs/web_test/test/karma/test-initialized.spec.js deleted file mode 100644 index 8b22e5c..0000000 --- a/packages/concatjs/web_test/test/karma/test-initialized.spec.js +++ /dev/null
@@ -1,12 +0,0 @@ -define('build_bazel_rules_nodejs/packages/concatjs/web_test/test/karma/test-initialized.spec', [], () => { - // Test that ensures that the "init-test.js" file has been included in the - // ConcatJS module and was actually executed by the browser. The "init-test.js" - // file is included in "static_files" and in the "deps" but should not be treated - // as static file as it is specified as dependency. - describe('Test initialization', () => { - - it('should have initialized tests', () => { - expect(window['__testInitialized']).toBe(true); - }); - }); -});
diff --git a/packages/concatjs/web_test/test/karma/tsconfig.json b/packages/concatjs/web_test/test/karma/tsconfig.json deleted file mode 100644 index e69de29..0000000 --- a/packages/concatjs/web_test/test/karma/tsconfig.json +++ /dev/null
diff --git a/packages/concatjs/web_test/test/karma/unnamed-amd-module.js b/packages/concatjs/web_test/test/karma/unnamed-amd-module.js deleted file mode 100644 index fd58f4e..0000000 --- a/packages/concatjs/web_test/test/karma/unnamed-amd-module.js +++ /dev/null
@@ -1,5 +0,0 @@ -// Unnamed AMD module definition which needs to be manually configured through -// a "requirejs" configuration whenever this module should be loaded. -define(function() { - // Just an empty definition. Nothing to assert here. -});
diff --git a/packages/concatjs/web_test/test/karma_typescript/BUILD.bazel b/packages/concatjs/web_test/test/karma_typescript/BUILD.bazel deleted file mode 100644 index b7c3e7b..0000000 --- a/packages/concatjs/web_test/test/karma_typescript/BUILD.bazel +++ /dev/null
@@ -1,118 +0,0 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -load("//packages/concatjs:index.bzl", "karma_web_test_suite", "ts_library") -load("//packages/jasmine:index.bzl", "jasmine_node_test") - -ts_library( - name = "lib", - srcs = ["decrement.ts"], -) - -ts_library( - name = "tests", - testonly = 1, - srcs = [ - "decrement.spec.ts", - "foobar.spec.ts", - ], - deps = [ - ":lib", - "@npm//@types/jasmine", - ], -) - -ts_library( - name = "hello_world", - testonly = 1, - srcs = [ - "hello_world.ts", - ], -) - -karma_web_test_suite( - name = "testing", - srcs = [ - # JS files only in srcs - eval'ed but not require'd - "foobar.js", - ], - browsers = [ - "@io_bazel_rules_webtesting//browsers:chromium-local", - "@io_bazel_rules_webtesting//browsers:firefox-local", - ], - tags = ["native"], - runtime_deps = [ - # typescript targets only in runtime_deps, must be TS module - ":hello_world", - ], - deps = [ - # JS file, must be AMD - "hello_world.spec.js", - # ts_library target, must be TS module - ":tests", - # filegroup target (not actually used in test code) - ":rxjs_umd_modules", - # node module. This dep has no effect, no files is collected. - "@npm//jasmine", - ], -) - -filegroup( - name = "rxjs_umd_modules", - srcs = [ - # do not sort - "@npm//:node_modules/rxjs/bundles/rxjs.umd.js", - ":rxjs_shims.js", - ], -) - -ts_library( - name = "coverage_test_srcs", - srcs = [ - "coverage_source.ts", - "coverage_source_uncovered.ts", - ], -) - -ts_library( - name = "coverage_test_spec_srcs", - srcs = ["coverage.spec.ts"], - deps = [ - ":coverage_test_srcs", - "@npm//@types/jasmine", - ], -) - -karma_web_test_suite( - name = "coverage_test", - browsers = [ - "@io_bazel_rules_webtesting//browsers:chromium-local", - "@io_bazel_rules_webtesting//browsers:firefox-local", - ], - tags = ["native"], - deps = [ - # ts_library target, must be TS module - ":coverage_test_spec_srcs", - ], -) - -jasmine_node_test( - name = "user_files_test", - srcs = [ - "user_files.spec.js", - ], - data = [ - ":testing_chromium-local", - ], -)
diff --git a/packages/concatjs/web_test/test/karma_typescript/coverage.spec.ts b/packages/concatjs/web_test/test/karma_typescript/coverage.spec.ts deleted file mode 100644 index 8a2ed42..0000000 --- a/packages/concatjs/web_test/test/karma_typescript/coverage.spec.ts +++ /dev/null
@@ -1,10 +0,0 @@ -import {isString} from './coverage_source'; - -describe('coverage function', () => { - it('should cover one branch', () => { - expect(isString(2 as any)).toBe(false); - }); - it('should cover the other branch', () => { - expect(isString('some string')).toBe(true); - }); -});
diff --git a/packages/concatjs/web_test/test/karma_typescript/coverage_source.ts b/packages/concatjs/web_test/test/karma_typescript/coverage_source.ts deleted file mode 100644 index 45f569b..0000000 --- a/packages/concatjs/web_test/test/karma_typescript/coverage_source.ts +++ /dev/null
@@ -1,7 +0,0 @@ -export function isString(input: string) { - if (typeof input === 'string') { - return true; - } else { - return false; - } -}
diff --git a/packages/concatjs/web_test/test/karma_typescript/coverage_source_uncovered.ts b/packages/concatjs/web_test/test/karma_typescript/coverage_source_uncovered.ts deleted file mode 100644 index a40d272..0000000 --- a/packages/concatjs/web_test/test/karma_typescript/coverage_source_uncovered.ts +++ /dev/null
@@ -1,6 +0,0 @@ -// noting in this file should be required, so we can test the c8 feature all: true -// which will pick up files that aren't directly referenced by test files -// but are added to coverage as empty coverage -export function notCalled(input: number) { - return input * 13; -} \ No newline at end of file
diff --git a/packages/concatjs/web_test/test/karma_typescript/decrement.spec.ts b/packages/concatjs/web_test/test/karma_typescript/decrement.spec.ts deleted file mode 100644 index bf1bd91..0000000 --- a/packages/concatjs/web_test/test/karma_typescript/decrement.spec.ts +++ /dev/null
@@ -1,7 +0,0 @@ -import {decrement} from './decrement'; - -describe('decrementing', () => { - it('should do that', () => { - expect(decrement(1)).toBe(0); - }); -});
diff --git a/packages/concatjs/web_test/test/karma_typescript/decrement.ts b/packages/concatjs/web_test/test/karma_typescript/decrement.ts deleted file mode 100644 index 7173ecc..0000000 --- a/packages/concatjs/web_test/test/karma_typescript/decrement.ts +++ /dev/null
@@ -1,3 +0,0 @@ -export function decrement(n: number) { - return n - 1; -}
diff --git a/packages/concatjs/web_test/test/karma_typescript/foobar.js b/packages/concatjs/web_test/test/karma_typescript/foobar.js deleted file mode 100644 index ff1dff5..0000000 --- a/packages/concatjs/web_test/test/karma_typescript/foobar.js +++ /dev/null
@@ -1 +0,0 @@ -window.foo = 'bar';
diff --git a/packages/concatjs/web_test/test/karma_typescript/foobar.spec.ts b/packages/concatjs/web_test/test/karma_typescript/foobar.spec.ts deleted file mode 100644 index 8624547..0000000 --- a/packages/concatjs/web_test/test/karma_typescript/foobar.spec.ts +++ /dev/null
@@ -1,8 +0,0 @@ -describe('foo', () => { - it('should be a global variable', () => { - expect((window as any).foo).toBe('bar'); - }); -}); - -// no-op export statement to make this file a typescript module. -export {};
diff --git a/packages/concatjs/web_test/test/karma_typescript/hello_world.spec.js b/packages/concatjs/web_test/test/karma_typescript/hello_world.spec.js deleted file mode 100644 index 198d5ed..0000000 --- a/packages/concatjs/web_test/test/karma_typescript/hello_world.spec.js +++ /dev/null
@@ -1,19 +0,0 @@ -(function (factory) { - if (typeof module === "object" && typeof module.exports === "object") { - var v = factory(require, exports); - if (v !== undefined) module.exports = v; - } - else if (typeof define === "function" && define.amd) { - define( - 'build_bazel_rules_nodejs/packages/concatjs/web_test/test/karma_typescript/hello_world.spec', - ['require', 'exports'], factory); - } -})(function (require, exports) { - "use strict"; - Object.defineProperty(exports, "__esModule", { value: true }); - describe('hello', () => { - it('should be a global variable', () => { - expect(window.hello).toBe('world'); - }); - }); -});
diff --git a/packages/concatjs/web_test/test/karma_typescript/hello_world.ts b/packages/concatjs/web_test/test/karma_typescript/hello_world.ts deleted file mode 100644 index c2de3ac..0000000 --- a/packages/concatjs/web_test/test/karma_typescript/hello_world.ts +++ /dev/null
@@ -1,4 +0,0 @@ -(window as any).hello = "world"; - -// no-op export statement to make this file a typescript module. -export {};
diff --git a/packages/concatjs/web_test/test/karma_typescript/rxjs_shims.js b/packages/concatjs/web_test/test/karma_typescript/rxjs_shims.js deleted file mode 100644 index 9a15da5..0000000 --- a/packages/concatjs/web_test/test/karma_typescript/rxjs_shims.js +++ /dev/null
@@ -1,35 +0,0 @@ -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -// rxjs/operators -(function(factory) { - if (typeof module === 'object' && typeof module.exports === 'object') { - var v = factory(require, exports); - if (v !== undefined) module.exports = v; - } else if (typeof define === 'function' && define.amd) { - define('rxjs/operators', ['exports', 'rxjs'], factory); - } -})(function(exports, rxjs) { - 'use strict'; - Object.keys(rxjs.operators).forEach(function(key) { exports[key] = rxjs.operators[key]; }); - Object.defineProperty(exports, '__esModule', {value: true}); -}); - -// rxjs/testing -(function(factory) { - if (typeof module === 'object' && typeof module.exports === 'object') { - var v = factory(require, exports); - if (v !== undefined) module.exports = v; - } else if (typeof define === 'function' && define.amd) { - define('rxjs/testing', ['exports', 'rxjs'], factory); - } -})(function(exports, rxjs) { - 'use strict'; - Object.keys(rxjs.testing).forEach(function(key) { exports[key] = rxjs.testing[key]; }); - Object.defineProperty(exports, '__esModule', {value: true}); -});
diff --git a/packages/concatjs/web_test/test/karma_typescript/user_files.spec.js b/packages/concatjs/web_test/test/karma_typescript/user_files.spec.js deleted file mode 100644 index 342ef42..0000000 --- a/packages/concatjs/web_test/test/karma_typescript/user_files.spec.js +++ /dev/null
@@ -1,48 +0,0 @@ -const fs = require('fs'); -const runfiles = require(process.env['BAZEL_NODE_RUNFILES_HELPER']); - -describe('karma_web_test_suite', () => { - let config; - - beforeAll(() => { - config = fs.readFileSync( - runfiles.resolve( - 'build_bazel_rules_nodejs/packages/concatjs/web_test/test/karma_typescript/testing_wrapped_test.conf.js'), - 'utf-8'); - }); - - it('should load default bootstrap files', () => { - const match = config.match(/\/\/ BEGIN BOOTSTRAP FILES(.*?)\/\/ END BOOTSTRAP FILES/s); - expect(match).toBeTruthy(); - const files = match.pop().split(',').map(l => { - // remove leading and trailing whitepaces, and begin quote and end quote. - return l.trim().slice(1, -1); - }).filter(l => !!l); - expect(files).toEqual([ - 'npm/node_modules/requirejs/require.js', - 'npm/node_modules/karma-requirejs/lib/adapter.js', - 'build_bazel_rules_nodejs/packages/concatjs/web_test/test/karma_typescript/_testing_wrapped_test.amd_names_shim.js', - ]); - }); - - it('should only collect node_sources and dev_scripts in user files', () => { - const match = config.match(/\/\/ BEGIN USER FILES(.*?)\/\/ END USER FILES/s); - expect(match).toBeTruthy(); - const files = match.pop().split(',').map(l => { - // remove leading and trailing whitepaces, and begin quote and end quote. - return l.trim().slice(1, -1); - }).filter(l => !!l); - // These are files that Karma should load, they are not necessarily - // topologically sorted. - expect(files).toEqual([ - 'build_bazel_rules_nodejs/packages/concatjs/web_test/test/karma_typescript/foobar.js', - 'build_bazel_rules_nodejs/packages/concatjs/web_test/test/karma_typescript/hello_world.spec.js', - 'build_bazel_rules_nodejs/packages/concatjs/web_test/test/karma_typescript/decrement.spec.js', - 'build_bazel_rules_nodejs/packages/concatjs/web_test/test/karma_typescript/foobar.spec.js', - 'build_bazel_rules_nodejs/packages/concatjs/web_test/test/karma_typescript/decrement.js', - 'npm/node_modules/rxjs/bundles/rxjs.umd.js', - 'build_bazel_rules_nodejs/packages/concatjs/web_test/test/karma_typescript/rxjs_shims.js', - 'build_bazel_rules_nodejs/packages/concatjs/web_test/test/karma_typescript/hello_world.js', - ]); - }); -});
diff --git a/packages/concatjs/web_test/test/stack_trace/BUILD.bazel b/packages/concatjs/web_test/test/stack_trace/BUILD.bazel deleted file mode 100644 index cb9244f..0000000 --- a/packages/concatjs/web_test/test/stack_trace/BUILD.bazel +++ /dev/null
@@ -1,56 +0,0 @@ -# Copyright 2019 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -load("//packages/concatjs:index.bzl", "karma_web_test_suite", "ts_library") - -ts_library( - name = "test_lib", - testonly = True, - srcs = glob(["*.spec.ts"]), - deps = [ - "@npm//@types/jasmine", - ], -) - -# This is a test with failing test. This test is not directly run by CI. -# The sh_test below invokes this test and checks the source mapped lines in the -# stack trace. -karma_web_test_suite( - name = "karma_test", - browsers = [ - "@io_bazel_rules_webtesting//browsers:chromium-local", - "@io_bazel_rules_webtesting//browsers:firefox-local", - ], - tags = [ - "manual", # not run by CI (see comment above) - "native", # rules_webtesting requires the "native" tag for browsers - ], - deps = [ - ":test_lib", - "//packages/concatjs/web_test/test/stack_trace/test_folder:test_lib", - ], -) - -sh_test( - name = "test_sourcemap", - srcs = ["test_sourcemap.sh"], - data = [ - ":karma_test_chromium-local", - "@build_bazel_rules_nodejs//third_party/github.com/bazelbuild/bazel/tools/bash/runfiles", - ], - tags = [ - "browser:chromium-local", - "fix-windows", - ], -)
diff --git a/packages/concatjs/web_test/test/stack_trace/failing.spec.ts b/packages/concatjs/web_test/test/stack_trace/failing.spec.ts deleted file mode 100644 index 5b77ed5..0000000 --- a/packages/concatjs/web_test/test/stack_trace/failing.spec.ts +++ /dev/null
@@ -1,9 +0,0 @@ -// This dummy export ensures that this file is compiled as a module instead -// of a script. -export {}; - -describe('stack trace', () => { - it('failing test', () => { - expect(true).toBe(false); - }); -});
diff --git a/packages/concatjs/web_test/test/stack_trace/test_folder/BUILD.bazel b/packages/concatjs/web_test/test/stack_trace/test_folder/BUILD.bazel deleted file mode 100644 index eda349b..0000000 --- a/packages/concatjs/web_test/test/stack_trace/test_folder/BUILD.bazel +++ /dev/null
@@ -1,35 +0,0 @@ -# Copyright 2019 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -load("//packages/concatjs:index.bzl", "ts_library") - -package(default_visibility = ["//visibility:public"]) - -ts_library( - name = "hello", - srcs = glob( - ["*.ts"], - exclude = ["*.spec.ts"], - ), -) - -ts_library( - name = "test_lib", - testonly = True, - srcs = glob(["*.spec.ts"]), - deps = [ - ":hello", - "@npm//@types/jasmine", - ], -)
diff --git a/packages/concatjs/web_test/test/stack_trace/test_folder/hello.ts b/packages/concatjs/web_test/test/stack_trace/test_folder/hello.ts deleted file mode 100644 index e7e5922..0000000 --- a/packages/concatjs/web_test/test/stack_trace/test_folder/hello.ts +++ /dev/null
@@ -1,7 +0,0 @@ -export function sayHello(): string { - return 'Hello'; -} - -export function throws(): string { - throw new Error('Error here'); -} \ No newline at end of file
diff --git a/packages/concatjs/web_test/test/stack_trace/test_folder/test.spec.ts b/packages/concatjs/web_test/test/stack_trace/test_folder/test.spec.ts deleted file mode 100644 index b847675..0000000 --- a/packages/concatjs/web_test/test/stack_trace/test_folder/test.spec.ts +++ /dev/null
@@ -1,11 +0,0 @@ -import {throws, sayHello} from './hello'; - -describe('multiple stack frames', () => { - it('failing test', () => { - expect(sayHello()).toBe('World'); - }); - - it('another failing test', () => { - expect(throws()).toBe('something'); - }); -});
diff --git a/packages/concatjs/web_test/test/stack_trace/test_sourcemap.sh b/packages/concatjs/web_test/test/stack_trace/test_sourcemap.sh deleted file mode 100755 index 3fa27f0..0000000 --- a/packages/concatjs/web_test/test/stack_trace/test_sourcemap.sh +++ /dev/null
@@ -1,26 +0,0 @@ -# Execute first test. -OUTPUT=$(${RUNFILES_DIR}/build_bazel_rules_nodejs/packages/concatjs/web_test/test/stack_trace/karma_test_chromium-local.sh) - -# Test whether the package relative TS path is printed in stack trace. -echo ${OUTPUT} | grep -q "(packages/concatjs/web_test/test/stack_trace/failing.spec.ts:7:18" -if [[ "$?" != "0" ]]; then - echo "Did not find '(packages/concatjs/web_test/test/stack_trace/failing.spec.ts:7:18' in Karma stack trace" - echo $OUTPUT - exit 1 -fi - -# Test whether the package relative path inside a subdirectory is printed. -echo ${OUTPUT} | grep -q "(packages/concatjs/web_test/test/stack_trace/test_folder/test.spec.ts:5:24" -if [[ "$?" != "0" ]]; then - echo "Did not find '(packages/concatjs/web_test/test/stack_trace/test_folder/test.spec.ts:5:24' in Karma stack trace" - exit 1 -fi - -# Test whether stack trace with multiple stack frames mapped get printed. -echo ${OUTPUT} | grep -q "(packages/concatjs/web_test/test/stack_trace/test_folder/hello.ts:6:9" -if [[ "$?" != "0" ]]; then - echo "Did not find '(packages/concatjs/web_test/test/stack_trace/test_folder/hello.ts:6:9' in Karma stack trace" - exit 1 -fi - -exit 0
diff --git a/packages/cypress/index.bzl b/packages/cypress/index.bzl index 4ac6b13..31ba139 100644 --- a/packages/cypress/index.bzl +++ b/packages/cypress/index.bzl
@@ -38,53 +38,6 @@ cypress_repositories(name = "cypress", version = "MATCH_VERSION_IN_PACKAGE_JSON") ``` - -## Example use of cypress_web_test -This example assumes you've named your external repository for node_modules as `npm` and for cypress as `cypress` -```python -load("@npm//@bazel/concatjs:index.bzl", "ts_library") -load("@npm//@bazel/cypress:index.bzl", "cypress_web_test") - -# You must create a cypress plugin in order to boot a server to serve your application. It can be written as a javascript file or in typescript using ts_library or ts_project. -ts_library( - name = "plugin_file", - testonly = True, - srcs = ["plugin.ts"], - tsconfig = ":tsconfig.json", - deps = [ - "@npm//@types/node", - "@npm//express", - ], -) - -# You can write your cypress tests a javascript files or in typescript using ts_library or ts_project. -ts_library( - name = "hello_spec", - testonly = True, - srcs = ["hello.spec.ts"], - tsconfig = ":tsconfig.json", - deps = [ - "@npm//cypress", - ], -) - -cypress_web_test( - # The name of your test target - name = "test", - srcs = [ - # Load javascript test files directly as sources - "world.spec.js", - # Load ts_library tests as a target to srcs - ":hello_spec", - ], - # A cypress config file is required - config_file = "cypress.json", - # Any runtime dependencies you need to boot your server or run your tests - data = [], - # Your cypress plugin used to configure cypress and boot your server - plugin_file = ":plugin_file", -) -``` """ load(
diff --git a/packages/index.bzl b/packages/index.bzl index 911ce96..31abd1c 100644 --- a/packages/index.bzl +++ b/packages/index.bzl
@@ -16,7 +16,6 @@ """ NPM_PACKAGES = ["@bazel/%s" % pkg for pkg in [ - "concatjs", "create", "jasmine", "protractor",
diff --git a/packages/typescript/index.docs.bzl b/packages/typescript/index.docs.bzl index 1e4bf2b..5ddb9f9 100644 --- a/packages/typescript/index.docs.bzl +++ b/packages/typescript/index.docs.bzl
@@ -106,11 +106,6 @@ - We control the output format and module syntax so that downstream rules can rely on them. - Some other options are incompatible. For example you cannot use the `--noEmit` compiler option in `tsconfig.json`. -The only reason to use `ts_library` for new code is if you are bought-in to using a [concatjs] bundler, which requires the named AMD module format. This may be faster than other tooling, and this format can be consumed by the Closure Compiler (via integration with [tsickle](https://github.com/angular/tsickle)). -However it is very challenging to configure and there is little available support for problems you'll run into. - -[concatjs]: https://www.npmjs.com/package/@bazel/concatjs - ## Installation Add a `devDependency` on `@bazel/typescript`