| // This program takes as inputs one or more TypeScript source files, determines whether they depend |
| // on any elements-sk modules, and writes to standard output a Sass stylesheet that imports the |
| // stylesheets required by any such elements-sk module. |
| // |
| // This program is used by the sk_element and sk_page Bazel rules to automatically generate a Sass |
| // stylesheet with any necessary elements-sk imports. This is necessary because the underlying |
| // Bazel rules ignore Webpack-style Sass imports from TypeScript files. |
| package main |
| |
| import ( |
| "fmt" |
| "io/ioutil" |
| "os" |
| "path/filepath" |
| "sort" |
| "strings" |
| |
| "go.skia.org/infra/bazel/gazelle/frontend/parsers" |
| "go.skia.org/infra/go/sklog" |
| "go.skia.org/infra/go/util" |
| ) |
| |
| // knownStylesheets is the list of Sass stylesheets we would like to import automatically based on |
| // the TypeScript imports found in a TypeScript source file. |
| // |
| // This list can be regenerated with the following command, which must be run from an elements-sk |
| // repository checkout: |
| // |
| // $ find src | grep \.scss$ | sed -E "s/^src/elements-sk/" | sort |
| var knownStylesheets = []string{ |
| "elements-sk/checkbox-sk/checkbox-sk.scss", |
| "elements-sk/collapse-sk/collapse-sk.scss", |
| "elements-sk/colors.scss", |
| "elements-sk/error-toast-sk/error-toast-sk.scss", |
| "elements-sk/icon/icon-sk.scss", |
| "elements-sk/multi-select-sk/multi-select-sk.scss", |
| "elements-sk/nav-links-sk/nav-links-sk.scss", |
| "elements-sk/radio-sk/radio-sk.scss", |
| "elements-sk/select-sk/select-sk.scss", |
| "elements-sk/spinner-sk/spinner-sk.scss", |
| "elements-sk/styles/buttons/buttons.scss", |
| "elements-sk/styles/select/select.scss", |
| "elements-sk/styles/table/table.scss", |
| "elements-sk/tabs-panel-sk/tabs-panel-sk.scss", |
| "elements-sk/tabs-sk/tabs-sk.scss", |
| "elements-sk/themes/color-palette.scss", |
| "elements-sk/themes/themes.scss", |
| "elements-sk/toast-sk/toast-sk.scss", |
| } |
| |
| // elementsSkStylesheetsFromTsImport takes the verbatim path of a TypeScript import statement, and |
| // if the path corresponds to an elements-sk module, returns the list of Sass stylesheets required |
| // by the elements-sk module, or nil otherwise. |
| func elementsSkStylesheetsFromTsImport(tsImport string) []string { |
| var stylesheets []string |
| |
| // Heuristic: if a TypeScript import is prefixed by the parent directory of a known |
| // stylesheet, we assume that the stylesheet must be imported as well. |
| // |
| // Example input TypeScript source file: |
| // |
| // // Resolves to "elements-sk/checkbox-sk/checkbox-sk.ts". |
| // import 'elements-sk/checkbox-sk/checkbox-sk'; |
| // |
| // // Resolves to "elements-sk/styles/buttons/index.ts". |
| // import 'elements-sk/styles/buttons'; |
| // |
| // Expected Sass imports: |
| // |
| // @import '~elements-sk/checkbox-sk/checkbox-sk'; |
| // @import '~elements-sk/styles/buttons/buttons'; |
| for _, stylesheet := range knownStylesheets { |
| // Exclude top-level stylesheets (e.g. "elements-sk/colors.scss"). |
| if filepath.Dir(stylesheet) == "elements-sk" { |
| continue |
| } |
| |
| if strings.HasPrefix(tsImport, filepath.Dir(stylesheet)) { |
| stylesheets = append(stylesheets, stylesheet) |
| } |
| } |
| |
| // In addition, we must account for dependencies between components. There are only a few |
| // such dependencies, and they change infrequently, so we handle them in an ad-hoc way. |
| if strings.HasPrefix(tsImport, "elements-sk/error-toast-sk") { |
| stylesheets = append(stylesheets, "elements-sk/toast-sk/toast-sk.scss") |
| } |
| if strings.HasPrefix(tsImport, "elements-sk/radio-sk") { |
| stylesheets = append(stylesheets, "elements-sk/checkbox-sk/checkbox-sk.scss") |
| } |
| |
| return stylesheets |
| } |
| |
| func main() { |
| tsSources := os.Args[1:] |
| if len(tsSources) == 0 { |
| sklog.Fatalf("Usage: %s <one or more TypeScript source files>", os.Args[0]) |
| } |
| |
| var outputSassImports []string |
| |
| // Iterate over all the input files. |
| for _, tsSource := range tsSources { |
| // Read in the current TypeScript source file, and extract the paths of its import statements. |
| b, err := ioutil.ReadFile(tsSource) |
| if err != nil { |
| sklog.Fatalf("Error while reading file %q: %v", tsSource, err) |
| } |
| tsImports := parsers.ParseTSImports(string(b)) |
| |
| // Map each TypeScript import to zero or more elements-sk Sass imports. |
| for _, tsImport := range tsImports { |
| for _, stylesheet := range elementsSkStylesheetsFromTsImport(tsImport) { |
| stylesheet = strings.TrimSuffix(stylesheet, filepath.Ext(stylesheet)) |
| outputSassImports = append(outputSassImports, fmt.Sprintf("@import '~%s';", stylesheet)) |
| } |
| } |
| } |
| |
| // Print out the Sass import statements for any required elements-sk stylesheets. |
| outputSassImports = util.SSliceDedup(outputSassImports) |
| sort.Strings(outputSassImports) |
| for _, imp := range outputSassImports { |
| fmt.Println(imp) |
| } |
| } |