Merge pull request #678 from trheyi/main

refactor: Update BuildContext components to use boolean values instea…
This commit is contained in:
Max 2024-07-12 08:42:44 +08:00 committed by GitHub
commit 181d592d53
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 241 additions and 169 deletions

View file

@ -12,6 +12,9 @@ import (
var slotRe = regexp.MustCompile(`\[\{([^\}]+)\}\]`)
var cssRe = regexp.MustCompile(`([\.a-z0-9A-Z-:# ]+)\{`)
var transStmtReSingle = regexp.MustCompile(`'::([^:']+)'`)
var transStmtReDouble = regexp.MustCompile(`"::([^:"]+)"`)
var transFuncRe = regexp.MustCompile(`__m\s*\(\s*["'](.*?)["']\s*\)`)
// Build build the page
func (page *Page) Build(ctx *BuildContext, option *BuildOption) (*goquery.Document, []string, error) {
@ -73,6 +76,27 @@ func (page *Page) Build(ctx *BuildContext, option *BuildOption) (*goquery.Docume
ctx.scripts = append(ctx.scripts, scripts...)
ctx.styles = append(ctx.styles, styles...)
// Add the translation marks
sequence := 0
err = page.TranslateMarks(ctx, doc, &sequence)
if err != nil {
return nil, warnings, err
}
// Translate the scripts
if (scripts != nil) && len(scripts) > 0 {
for _, script := range scripts {
if script.Source == "" {
continue
}
trans, _, err := page.translateScript(script.Source, &sequence)
if err != nil {
return nil, ctx.warnings, err
}
ctx.translations = append(ctx.translations, trans...)
}
}
return doc, ctx.warnings, err
}
@ -83,6 +107,10 @@ func (page *Page) BuildAsComponent(sel *goquery.Selection, ctx *BuildContext, op
return "", fmt.Errorf("The parent page is not set")
}
if ctx == nil {
ctx = NewBuildContext(nil)
}
// Push the current page onto the stack and increment the visit counter
ctx.stack = append(ctx.stack, page.Route)
ctx.visited[page.Route]++
@ -151,11 +179,34 @@ func (page *Page) BuildAsComponent(sel *goquery.Selection, ctx *BuildContext, op
page.buildComponents(doc, ctx, &opt)
data := Data{"$props": page.Attrs}
data.ReplaceSelectionUse(slotRe, first)
// Add the translation marks
sequence := 0
err = page.TranslateMarks(ctx, doc, &sequence)
if err != nil {
return "", err
}
// Translate the scripts
if (scripts != nil) && len(scripts) > 0 {
for _, script := range scripts {
if script.Source == "" {
continue
}
trans, _, err := page.translateScript(script.Source, &sequence)
if err != nil {
return "", err
}
ctx.translations = append(ctx.translations, trans...)
}
}
html, err = body.Html()
if err != nil {
return "", err
}
sel.ReplaceWithHtml(html)
ctx.components[page.Route] = true
return html, nil
}
@ -496,3 +547,162 @@ func addTabToEachLine(input string, prefix ...string) string {
return strings.Join(lines, "\n")
}
// TranslateMarks add the translation marks to the document
func (page *Page) TranslateMarks(ctx *BuildContext, doc *goquery.Document, sequence *int) error {
if doc.Length() == 0 {
return nil
}
if ctx == nil {
ctx = NewBuildContext(nil)
}
if ctx.translations == nil {
ctx.translations = []Translation{}
}
root := doc.First()
translations, err := page.translateNode(root.Nodes[0], sequence)
if err != nil {
return err
}
if translations != nil {
ctx.translations = append(ctx.translations, translations...)
}
return nil
}
func (page *Page) translateNode(node *html.Node, sequence *int) ([]Translation, error) {
translations := []Translation{}
*sequence = *sequence + 1
switch node.Type {
case html.DocumentNode:
for child := node.FirstChild; child != nil; child = child.NextSibling {
trans, err := page.translateNode(child, sequence)
if err != nil {
return nil, err
}
translations = append(translations, trans...)
}
break
case html.ElementNode:
// Script
if node.Data == "script" {
code := goquery.NewDocumentFromNode(node).Text()
trans, _, err := page.translateScript(code, sequence)
if err != nil {
return nil, err
}
translations = append(translations, trans...)
break
}
sel := goquery.NewDocumentFromNode(node)
for _, attr := range node.Attr {
trans, keys, err := page.translateText(attr.Val, sequence, "attr")
if err != nil {
return nil, err
}
if len(keys) > 0 {
raw := strings.Join(keys, ",")
sel.SetAttr("s:trans-attr-"+attr.Key, raw)
translations = append(translations, trans...)
}
}
// Node Attributes
for child := node.FirstChild; child != nil; child = child.NextSibling {
trans, err := page.translateNode(child, sequence)
if err != nil {
return nil, err
}
translations = append(translations, trans...)
}
break
case html.TextNode:
parentSel := goquery.NewDocumentFromNode(node.Parent)
if _, has := parentSel.Attr("s:trans"); has {
key := TranslationKey(page.Route, *sequence)
message := strings.TrimSpace(node.Data)
if message != "" {
translations = append(translations, Translation{
Key: key,
Message: message,
Type: "text",
})
parentSel.SetAttr("s:trans-node", key)
*sequence = *sequence + 1
}
parentSel.RemoveAttr("s:trans")
}
trans, keys, err := page.translateText(node.Data, sequence, "text")
if err != nil {
return nil, err
}
if len(keys) > 0 {
raw := strings.Join(keys, ",")
parentSel.SetAttr("s:trans-text", raw)
parentSel.RemoveAttr("s:trans")
translations = append(translations, trans...)
}
break
}
return translations, nil
}
func (page *Page) translateText(text string, sequence *int, transType string) ([]Translation, []string, error) {
translations := []Translation{}
matches := stmtRe.FindAllStringSubmatch(text, -1)
keys := []string{}
for _, match := range matches {
text := strings.TrimSpace(match[1])
transMatches := transStmtReSingle.FindAllStringSubmatch(text, -1)
if len(transMatches) == 0 {
transMatches = transStmtReDouble.FindAllStringSubmatch(text, -1)
}
for _, transMatch := range transMatches {
message := strings.TrimSpace(transMatch[1])
key := TranslationKey(page.Route, *sequence)
keys = append(keys, key)
translations = append(translations, Translation{
Key: key,
Message: message,
Type: transType,
})
*sequence = *sequence + 1
}
}
return translations, keys, nil
}
func (page *Page) translateScript(code string, sequence *int) ([]Translation, []string, error) {
translations := []Translation{}
keys := []string{}
if code == "" {
return translations, keys, nil
}
matches := transFuncRe.FindAllStringSubmatch(code, -1)
for _, match := range matches {
key := TranslationKey(page.Route, *sequence)
translations = append(translations, Translation{
Key: key,
Message: match[1],
Type: "script",
})
*sequence = *sequence + 1
}
return translations, keys, nil
}

View file

@ -5,20 +5,15 @@ import (
"regexp"
"strings"
"github.com/PuerkitoBio/goquery"
"github.com/evanw/esbuild/pkg/api"
jsoniter "github.com/json-iterator/go"
"github.com/yaoapp/gou/runtime/transform"
"github.com/yaoapp/kun/log"
"golang.org/x/net/html"
)
var quoteRe = "'\"`"
var importRe = regexp.MustCompile(`import\s*\t*\n*[^;]*;`) // import { foo, bar } from 'hello'; ...
var importAssetsRe = regexp.MustCompile(`import\s*\t*\n*\s*['"]@assets\/([^'"]+)['"];`) // import '@assets/foo.js'; or import "@assets/foo.js";
var transStmtReSingle = regexp.MustCompile(`'::([^:']+)'`)
var transStmtReDouble = regexp.MustCompile(`"::([^:"]+)"`)
var transFuncRe = regexp.MustCompile(`__m\s*\(\s*["'](.*?)["']\s*\)`)
// AssetsRe is the regexp for assets
var AssetsRe = regexp.MustCompile(`[` + quoteRe + `]@assets\/([^` + quoteRe + `]+)[` + quoteRe + `]`) // '@assets/foo.js' or "@assets/foo.js" or `@assets/foo`
@ -90,12 +85,15 @@ func (page *Page) Compile(ctx *BuildContext, option *BuildOption) (string, []str
)
}
// Add the translation marks
sequence := 0
err = page.TranslateMarks(ctx, option, doc, &sequence)
if err != nil {
return "", warnings, err
// Page Components
components := []string{}
if ctx != nil && ctx.components != nil && len(ctx.components) > 0 {
for route := range ctx.components {
components = append(components, route)
}
}
rawComponents, _ := jsoniter.MarshalToString(components)
body.AppendHtml("\n\n" + `<script name="imports" type="json">` + "\n" + rawComponents + "\n</script>\n\n")
page.ReplaceDocument(doc)
html, err := doc.Html()
@ -140,6 +138,15 @@ func (page *Page) CompileAsComponent(ctx *BuildContext, option *BuildOption) (st
return "", warnings, err
}
// Page Components
components := []string{}
if ctx != nil && ctx.components != nil && len(ctx.components) > 0 {
for route := range ctx.components {
components = append(components, route)
}
}
rawComponents, _ := jsoniter.MarshalToString(components)
if body.Children().Length() == 0 {
return "", warnings, fmt.Errorf("page %s as component should have one root element", page.Route)
}
@ -151,13 +158,7 @@ func (page *Page) CompileAsComponent(ctx *BuildContext, option *BuildOption) (st
body.Children().First().AppendHtml(fmt.Sprintf(`<script name="scripts" type="json">%s</script>`+"\n", rawScripts))
body.Children().First().AppendHtml(fmt.Sprintf(`<script name="styles" type="json">%s</script>`+"\n", rawStyles))
body.Children().First().AppendHtml(fmt.Sprintf(`<script name="option" type="json">%s</script>`+"\n", rawOption))
// Add the translation marks
sequence := 0
err = page.TranslateMarks(ctx, option, doc, &sequence)
if err != nil {
return "", warnings, err
}
body.Children().First().AppendHtml(fmt.Sprintf(`<script name="imports" type="json">%s</script>`+"\n", rawComponents))
html, err := body.Html()
return html, warnings, err
@ -282,150 +283,3 @@ func (style StyleNode) HTML() string {
return "<style " + strings.Join(attrs, " ") + ">\n" + style.Source + "\n</style>"
}
// TranslateMarks add the translation marks to the document
func (page *Page) TranslateMarks(ctx *BuildContext, option *BuildOption, doc *goquery.Document, sequence *int) error {
if doc.Length() == 0 {
return nil
}
if ctx == nil {
ctx = NewBuildContext(nil)
}
if ctx.translations == nil {
ctx.translations = []Translation{}
}
root := doc.First()
translations, err := page.translateNode(root.Nodes[0], sequence)
if err != nil {
return err
}
if translations != nil {
ctx.translations = append(ctx.translations, translations...)
}
return nil
}
func (page *Page) translateNode(node *html.Node, sequence *int) ([]Translation, error) {
translations := []Translation{}
*sequence = *sequence + 1
switch node.Type {
case html.DocumentNode:
for child := node.FirstChild; child != nil; child = child.NextSibling {
trans, err := page.translateNode(child, sequence)
if err != nil {
return nil, err
}
translations = append(translations, trans...)
}
break
case html.ElementNode:
// Script
if node.Data == "script" {
code := goquery.NewDocumentFromNode(node).Text()
if code != "" {
translations := []Translation{}
matches := transFuncRe.FindAllStringSubmatch(code, -1)
for _, match := range matches {
key := Namespace(page.Route, *sequence)
translations = append(translations, Translation{
Key: key,
Message: match[1],
Type: "script",
})
*sequence = *sequence + 1
}
}
break
}
sel := goquery.NewDocumentFromNode(node)
for _, attr := range node.Attr {
trans, keys, err := page.translateText(attr.Val, sequence, "attr")
if err != nil {
return nil, err
}
if len(keys) > 0 {
raw := strings.Join(keys, ",")
sel.SetAttr("s:trans-attr-"+attr.Key, raw)
translations = append(translations, trans...)
}
}
// Node Attributes
for child := node.FirstChild; child != nil; child = child.NextSibling {
trans, err := page.translateNode(child, sequence)
if err != nil {
return nil, err
}
translations = append(translations, trans...)
}
break
case html.TextNode:
parentSel := goquery.NewDocumentFromNode(node.Parent)
if _, has := parentSel.Attr("s:trans"); has {
key := Namespace(page.Route, *sequence)
message := strings.TrimSpace(node.Data)
if message != "" {
translations = append(translations, Translation{
Key: key,
Message: message,
Type: "text",
})
parentSel.SetAttr("s:trans-node", key)
*sequence = *sequence + 1
}
parentSel.RemoveAttr("s:trans")
}
trans, keys, err := page.translateText(node.Data, sequence, "text")
if err != nil {
return nil, err
}
if len(keys) > 0 {
raw := strings.Join(keys, ",")
parentSel.SetAttr("s:trans-text", raw)
parentSel.RemoveAttr("s:trans")
translations = append(translations, trans...)
}
break
}
return translations, nil
}
func (page *Page) translateText(text string, sequence *int, typ string) ([]Translation, []string, error) {
translations := []Translation{}
matches := stmtRe.FindAllStringSubmatch(text, -1)
keys := []string{}
for _, match := range matches {
text := strings.TrimSpace(match[1])
transMatches := transStmtReSingle.FindAllStringSubmatch(text, -1)
if len(transMatches) == 0 {
transMatches = transStmtReDouble.FindAllStringSubmatch(text, -1)
}
for _, transMatch := range transMatches {
message := strings.TrimSpace(transMatch[1])
key := Namespace(page.Route, *sequence)
keys = append(keys, key)
translations = append(translations, Translation{
Key: key,
Message: message,
Type: typ,
})
*sequence = *sequence + 1
}
}
return translations, keys, nil
}

View file

@ -3,7 +3,7 @@ package core
// NewBuildContext create a new build context
func NewBuildContext(global *GlobalBuildContext) *BuildContext {
return &BuildContext{
components: map[string]string{},
components: map[string]bool{},
sequence: 1,
scripts: []ScriptNode{},
scriptUnique: map[string]bool{},

View file

@ -47,7 +47,7 @@ type Page struct {
// BuildContext is the struct for the build context
type BuildContext struct {
components map[string]string
components map[string]bool
jitComponents map[string]bool
sequence int
doc *goquery.Document

View file

@ -55,13 +55,13 @@ func Namespace(name string, idx int, hash ...bool) string {
name = strings.ReplaceAll(name, "/", "_")
name = strings.ReplaceAll(name, "[", "_")
name = strings.ReplaceAll(name, "]", "_")
ns := fmt.Sprintf("__namespace_%s", name)
ns := fmt.Sprintf("page_%s_%d", name, idx)
if len(hash) > 0 && hash[0] {
h := fnv.New64a()
h.Write([]byte(ns))
return fmt.Sprintf("ns_%x", h.Sum64())
}
return fmt.Sprintf("__page_%s_%d", name, idx)
return ns
}
// ComponentName convert the name to component name
@ -69,7 +69,7 @@ func ComponentName(name string, hash ...bool) string {
name = strings.ReplaceAll(name, "/", "_")
name = strings.ReplaceAll(name, "[", "_")
name = strings.ReplaceAll(name, "]", "_")
cn := fmt.Sprintf("__component_%s", name)
cn := fmt.Sprintf("comp_%s", name)
if len(hash) > 0 && hash[0] {
h := fnv.New64a()
h.Write([]byte(cn))
@ -77,3 +77,11 @@ func ComponentName(name string, hash ...bool) string {
}
return cn
}
// TranslationKey convert the name to translation key
func TranslationKey(name string, sequence int) string {
name = strings.ReplaceAll(name, "/", "_")
name = strings.ReplaceAll(name, "[", "_")
name = strings.ReplaceAll(name, "]", "_")
return fmt.Sprintf("trans_%s_%d", name, sequence)
}