diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..a24c671 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,113 @@ +# This file was automatically generated by sbt-github-actions using the +# githubWorkflowGenerate task. You should add and commit this file to +# your git repository. It goes without saying that you shouldn't edit +# this file by hand! Instead, if you wish to make changes, you should +# change your sbt build configuration to revise the workflow description +# to meet your needs, then regenerate this file. + +name: Continuous Integration + +on: + pull_request: + branches: ['**'] + push: + branches: ['**'] + tags: [v*] + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +jobs: + build: + name: Build and Test + strategy: + matrix: + os: [ubuntu-latest] + scala: [2.13.x, 3.3.x] + java: [temurin@11] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout current branch (full) + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Java (temurin@11) + if: matrix.java == 'temurin@11' + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 11 + cache: sbt + + - name: Setup sbt + uses: sbt/setup-sbt@v1 + + - name: Check that workflows are up to date + run: sbt '++ ${{ matrix.scala }}' githubWorkflowCheck + + - name: Build project + run: sbt '++ ${{ matrix.scala }}' test + + - name: Compress target directories + run: tar cf targets.tar bootstrap3_scalatags/target fomanticui_scalatags/target bootstrap4_scalatags/target bulma_scalatags/target bootstrap3_scalajsreact/target fontawesome_scalatags/target bootstrap5_scalajsreact/target target bulma_scalajsreact/target bootstrap4_scalajsreact/target fontawesome_scalajsreact/target semanticui_scalajsreact/target semanticui_scalatags/target fomanticui_scalajsreact/target bootstrap5_scalatags/target project/target + + - name: Upload target directories + uses: actions/upload-artifact@v4 + with: + name: target-${{ matrix.os }}-${{ matrix.scala }}-${{ matrix.java }} + path: targets.tar + + publish: + name: Publish Artifacts + needs: [build] + if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v')) + strategy: + matrix: + os: [ubuntu-latest] + scala: [3.3.6] + java: [temurin@11] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout current branch (full) + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Java (temurin@11) + if: matrix.java == 'temurin@11' + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 11 + cache: sbt + + - name: Setup sbt + uses: sbt/setup-sbt@v1 + + - name: Download target directories (2.13.x) + uses: actions/download-artifact@v4 + with: + name: target-${{ matrix.os }}-2.13.x-${{ matrix.java }} + + - name: Inflate target directories (2.13.x) + run: | + tar xf targets.tar + rm targets.tar + + - name: Download target directories (3.3.x) + uses: actions/download-artifact@v4 + with: + name: target-${{ matrix.os }}-3.3.x-${{ matrix.java }} + + - name: Inflate target directories (3.3.x) + run: | + tar xf targets.tar + rm targets.tar + + - env: + PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} + PGP_SECRET: ${{ secrets.PGP_SECRET }} + SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} + run: sbt ci-release diff --git a/.github/workflows/clean.yml b/.github/workflows/clean.yml new file mode 100644 index 0000000..bfc865d --- /dev/null +++ b/.github/workflows/clean.yml @@ -0,0 +1,60 @@ +# This file was automatically generated by sbt-github-actions using the +# githubWorkflowGenerate task. You should add and commit this file to +# your git repository. It goes without saying that you shouldn't edit +# this file by hand! Instead, if you wish to make changes, you should +# change your sbt build configuration to revise the workflow description +# to meet your needs, then regenerate this file. + +name: Clean + +on: push + +jobs: + delete-artifacts: + name: Delete Artifacts + runs-on: ubuntu-latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: Delete artifacts + shell: bash {0} + run: | + # Customize those three lines with your repository and credentials: + REPO=${GITHUB_API_URL}/repos/${{ github.repository }} + + # A shortcut to call GitHub API. + ghapi() { curl --silent --location --user _:$GITHUB_TOKEN "$@"; } + + # A temporary file which receives HTTP response headers. + TMPFILE=$(mktemp) + + # An associative array, key: artifact name, value: number of artifacts of that name. + declare -A ARTCOUNT + + # Process all artifacts on this repository, loop on returned "pages". + URL=$REPO/actions/artifacts + while [[ -n "$URL" ]]; do + + # Get current page, get response headers in a temporary file. + JSON=$(ghapi --dump-header $TMPFILE "$URL") + + # Get URL of next page. Will be empty if we are at the last page. + URL=$(grep '^Link:' "$TMPFILE" | tr ',' '\n' | grep 'rel="next"' | head -1 | sed -e 's/.*.*//') + rm -f $TMPFILE + + # Number of artifacts on this page: + COUNT=$(( $(jq <<<$JSON -r '.artifacts | length') )) + + # Loop on all artifacts on this page. + for ((i=0; $i < $COUNT; i++)); do + + # Get name of artifact and count instances of this name. + name=$(jq <<<$JSON -r ".artifacts[$i].name?") + ARTCOUNT[$name]=$(( $(( ${ARTCOUNT[$name]} )) + 1)) + + id=$(jq <<<$JSON -r ".artifacts[$i].id?") + size=$(( $(jq <<<$JSON -r ".artifacts[$i].size_in_bytes?") )) + printf "Deleting '%s' #%d, %'d bytes\n" $name ${ARTCOUNT[$name]} $size + ghapi -X DELETE $REPO/actions/artifacts/$id + done + done diff --git a/.gitignore b/.gitignore index 92322c4..face47b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ .idea/ target/ +/.bsp/ +/.metals/ +/.bloop/ diff --git a/.mergify.yml b/.mergify.yml new file mode 100644 index 0000000..1f7ae6d --- /dev/null +++ b/.mergify.yml @@ -0,0 +1,15 @@ +defaults: {} +queue_rules: + - name: default + merge_conditions: [] +pull_request_rules: + - name: Automatically merge successful Scala Steward PRs + conditions: + - or: + - author=scala-steward + - author=nafg-scala-steward[bot] + - check-success=Build and Test (ubuntu-latest, 2.13.x, temurin@11) + - check-success=Build and Test (ubuntu-latest, 3.3.x, temurin@11) + actions: + queue: + name: default diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 22165c3..0000000 --- a/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -jdk: - - openjdk8 -language: scala -script: "sbt +test" -deploy: - provider: script - script: sbt +publish - on: - tags: true diff --git a/README.md b/README.md index d5e2a16..58f0b40 100644 --- a/README.md +++ b/README.md @@ -23,12 +23,13 @@ write code like this: ``` ## Variants -### CSS Frameworks +### CSS libraries * Bootstrap 3 * Bootstrap 4 * Bulma * Semantic UI * Fomantic UI + * Font Awesome ### Targeted Libraries * Scalajs-react @@ -47,36 +48,51 @@ Additionally, most frameworks are available with prefixed and unprefixed methods #### Resolver Artifacts are published to Bintray and synced to Bintray JCenter. For SBT use `resolvers += Resolver.jcenterRepo` or `useJCenter := true` (prefixed with `ThisBuild / ` if needed). For other build tools add https://jcenter.bintray.com as a maven repository. + + #### Artifact -| CSS Framework | Library | SBT module ID | -|---------------|--------------------------|--------------------------------------------------------------------| -| Bootstrap 3 | `scalatags.Text` (JVM) | `"io.github.nafg.css-dsl" %% "bootstrap3_scalatags" % "0.6.0"` | -| Bootstrap 3 | scalajs-react (scala.js) | `"io.github.nafg.css-dsl" %%% "bootstrap3_scalajsreact" % "0.6.0"` | -| Bootstrap 4 | `scalatags.Text` (JVM) | `"io.github.nafg.css-dsl" %% "bootstrap4_scalatags" % "0.6.0"` | -| Bootstrap 4 | scalajs-react (scala.js) | `"io.github.nafg.css-dsl" %%% "bootstrap4_scalajsreact" % "0.6.0"` | -| Bulma | `scalatags.Text` (JVM) | `"io.github.nafg.css-dsl" %% "bulma_scalatags" % "0.6.0"` | -| Bulma | scalajs-react (scala.js) | `"io.github.nafg.css-dsl" %%% "bulma_scalajsreact" % "0.6.0"` | -| Semantic UI | `scalatags.Text` (JVM) | `"io.github.nafg.css-dsl" %% "semanticui_scalatags" % "0.6.0"` | -| Semantic UI | scalajs-react (scala.js) | `"io.github.nafg.css-dsl" %%% "semanticui_scalajsreact" % "0.6.0"` | -| Fomantic UI | `scalatags.Text` (JVM) | `"io.github.nafg.css-dsl" %% "fomanticui_scalatags" % "0.6.0"` | -| Fomantic UI | scalajs-react (scala.js) | `"io.github.nafg.css-dsl" %%% "fomanticui_scalajsreact" % "0.6.0"` | +| CSS library | Scala DOM library | SBT Module ID | +|--------------|--------------------------|---------------------------------------------------------------------| +| Bootstrap 3 | `scalatags.Text` (JVM) | `"io.github.nafg.css-dsl" %% "bootstrap3_scalatags" % "0.9.0"` | +| Bootstrap 3 | scalajs-react (scala.js) | `"io.github.nafg.css-dsl" %%% "bootstrap3_scalajsreact" % "0.9.0"` | +| Bootstrap 4 | `scalatags.Text` (JVM) | `"io.github.nafg.css-dsl" %% "bootstrap4_scalatags" % "0.9.0"` | +| Bootstrap 4 | scalajs-react (scala.js) | `"io.github.nafg.css-dsl" %%% "bootstrap4_scalajsreact" % "0.9.0"` | +| Bootstrap 5 | `scalatags.Text` (JVM) | `"io.github.nafg.css-dsl" %% "bootstrap5_scalatags" % "0.9.0"` | +| Bootstrap 5 | scalajs-react (scala.js) | `"io.github.nafg.css-dsl" %%% "bootstrap5_scalajsreact" % "0.9.0"` | +| Bulma | `scalatags.Text` (JVM) | `"io.github.nafg.css-dsl" %% "bulma_scalatags" % "0.9.0"` | +| Bulma | scalajs-react (scala.js) | `"io.github.nafg.css-dsl" %%% "bulma_scalajsreact" % "0.9.0"` | +| Fomantic UI | `scalatags.Text` (JVM) | `"io.github.nafg.css-dsl" %% "fomanticui_scalatags" % "0.9.0"` | +| Fomantic UI | scalajs-react (scala.js) | `"io.github.nafg.css-dsl" %%% "fomanticui_scalajsreact" % "0.9.0"` | +| Font Awesome | `scalatags.Text` (JVM) | `"io.github.nafg.css-dsl" %% "fontawesome_scalatags" % "0.9.0"` | +| Font Awesome | scalajs-react (scala.js) | `"io.github.nafg.css-dsl" %%% "fontawesome_scalajsreact" % "0.9.0"` | +| Semantic UI | `scalatags.Text` (JVM) | `"io.github.nafg.css-dsl" %% "semanticui_scalatags" % "0.9.0"` | +| Semantic UI | scalajs-react (scala.js) | `"io.github.nafg.css-dsl" %%% "semanticui_scalajsreact" % "0.9.0"` | ### Import -| Framework | Prefix | Import | -|-------------|--------|-------------------------------------| -| Bootstrap 3 | None | `import cssdsl.bootstrap3.Dsl._` | -| Bootstrap 3 | `bs` | `import cssdsl.bootstrap3.BsDsl._` | -| Bootstrap 3 | `bs3` | `import cssdsl.bootstrap3.Bs3Dsl._` | -| Bootstrap 4 | None | `import cssdsl.bootstrap4.Dsl._` | -| Bootstrap 4 | `bs` | `import cssdsl.bootstrap4.BsDsl._` | -| Bootstrap 4 | `bs4` | `import cssdsl.bootstrap4.Bs4Dsl._` | -| Bulma | None | `import cssdsl.bulma.Dsl._` | -| Bulma | `b` | `import cssdsl.bulma.BDsl._` | -| Semantic UI | `s` | `import cssdsl.semanticui.SDsl._` | -| Fomantic UI | `f` | `import cssdsl.fomanticui.FDsl._` | +| CSS library | Prefix | Import | +|--------------|--------|-------------------------------------| +| Bootstrap 3 | None | `import cssdsl.bootstrap3.Dsl._` | +| Bootstrap 3 | `bs` | `import cssdsl.bootstrap3.BsDsl._` | +| Bootstrap 3 | `bs3` | `import cssdsl.bootstrap3.Bs3Dsl._` | +| Bootstrap 4 | None | `import cssdsl.bootstrap4.Dsl._` | +| Bootstrap 4 | `bs` | `import cssdsl.bootstrap4.BsDsl._` | +| Bootstrap 4 | `bs4` | `import cssdsl.bootstrap4.Bs4Dsl._` | +| Bootstrap 5 | None | `import cssdsl.bootstrap5.Dsl._` | +| Bootstrap 5 | `bs` | `import cssdsl.bootstrap5.BsDsl._` | +| Bootstrap 5 | `bs5` | `import cssdsl.bootstrap5.Bs5Dsl._` | +| Bulma | None | `import cssdsl.bulma.Dsl._` | +| Bulma | `b` | `import cssdsl.bulma.BDsl._` | +| Fomantic UI | `f` | `import cssdsl.fomanticui.FDsl._` | +| Font Awesome | None | `import cssdsl.fontawesome.Dsl._` | +| Font Awesome | `fa` | `import cssdsl.fontawesome.FaDsl._` | +| Semantic UI | `s` | `import cssdsl.semanticui.SDsl._` | + + + + ### Code diff --git a/bintray.sbt b/bintray.sbt deleted file mode 100644 index fb154da..0000000 --- a/bintray.sbt +++ /dev/null @@ -1,11 +0,0 @@ -ThisBuild / publishMavenStyle := true -ThisBuild / publishTo := Some("bintray" at "https://api.bintray.com/maven/naftoligug/maven/css-dsl") - -sys.env.get("BINTRAYKEY").toSeq.map { key => - ThisBuild / credentials += Credentials( - "Bintray API Realm", - "api.bintray.com", - "naftoligug", - key - ) -} diff --git a/build.sbt b/build.sbt index 5528b07..980d67a 100644 --- a/build.sbt +++ b/build.sbt @@ -1,65 +1,109 @@ -ThisBuild / organization := "io.github.nafg.css-dsl" +import scala.sys.process.stringToProcess +import _root_.io.github.nafg.mergify.dsl._ +import _root_.io.github.nafg.scalacoptions._ -ThisBuild / crossScalaVersions := Seq("2.12.11", "2.13.2") -ThisBuild / scalaVersion := (ThisBuild / crossScalaVersions).value.last -ThisBuild / scalacOptions += "-feature" -ThisBuild / scalacOptions += "-deprecation" + +def myScalacOptions(version: String) = + ScalacOptions.all(version)( + (opts: options.Common) => opts.feature ++ opts.deprecation, + ) + +inThisBuild( + List( + organization := "io.github.nafg.css-dsl", + scalaVersion := "3.3.6", + crossScalaVersions := Seq("2.13.16", scalaVersion.value), + scalacOptions ++= myScalacOptions(scalaVersion.value), + versionScheme := Some("early-semver") + ) +) name := "css-dsl" publish / skip := true +mergifyExtraConditions := Seq( + (Attr.Author :== "scala-steward") || + (Attr.Author :== "nafg-scala-steward[bot]") +) + +def npmView(pkg: String, field: String)(parse: Stream[String] => String) = + parse(s"npm view $pkg $field".lineStream) + +def latestTag(pkg: String) = npmView(pkg, "dist-tags.latest")(_.head) + +val npmViewVersionRegex = ".*'(.*)'".r + +def latestIn(pkg: String, versionMajor: Int) = + npmView(s"$pkg@$versionMajor", "version")(_.last match { case npmViewVersionRegex(v) => v }) + def scalaJsReactSettings(config: CssDslConfig) = Seq( - libraryDependencies += "com.github.japgolly.scalajs-react" %%% "core" % "1.7.0", + libraryDependencies += "com.github.japgolly.scalajs-react" %%% "core" % "2.1.2", cssVariant := TargetImpl.ScalaJsReact, cssDslConfig := config ) def scalatagsSettings(config: CssDslConfig) = Seq( - libraryDependencies += "com.lihaoyi" %%% "scalatags" % "0.9.1", + libraryDependencies += "com.lihaoyi" %%% "scalatags" % "0.13.1", cssVariant := TargetImpl.Scalatags, cssDslConfig := config ) val bootstrap3Config = CssDslConfig( - "bootstrap3", + "Bootstrap 3", Set(None, Some("bs"), Some("bs3")), - "3.4.1", - "https://maxcdn.bootstrapcdn.com/bootstrap/" + _ + "/css/bootstrap.min.css" + latestIn("bootstrap", 3), + "https://stackpath.bootstrapcdn.com/bootstrap/" + _ + "/css/bootstrap.min.css" ) val bootstrap4Config = CssDslConfig( - "bootstrap4", + "Bootstrap 4", Set(None, Some("bs"), Some("bs4")), - "4.4.1", - "https://maxcdn.bootstrapcdn.com/bootstrap/" + _ + "/css/bootstrap.min.css" + latestIn("bootstrap", 4), + "https://cdn.jsdelivr.net/npm/bootstrap@" + _ + "/dist/css/bootstrap.min.css" + ) + +val bootstrap5Config = + CssDslConfig( + "Bootstrap 5", + Set(None, Some("bs"), Some("bs5")), + latestIn("bootstrap", 5), + "https://cdn.jsdelivr.net/npm/bootstrap@" + _ + "/dist/css/bootstrap.min.css" ) val bulmaConfig = CssDslConfig( - "bulma", + "Bulma", Set(None, Some("b")), - "0.8.2", - "https://cdnjs.cloudflare.com/ajax/libs/bulma/" + _ + "/css/bulma.css" + latestTag("bulma"), + "https://cdn.jsdelivr.net/npm/bulma@" + _ + "/css/bulma.min.css" ) val semanticUiConfig = CssDslConfig( - "semanticui", + "Semantic UI", Set(Some("s")), - "2.4.2", + latestTag("semantic-ui"), "https://cdn.jsdelivr.net/npm/semantic-ui@" + _ + "/dist/semantic.min.css" ) val fomanticUiConfig = CssDslConfig( - "fomanticui", + "Fomantic UI", Set(Some("f")), - "2.8.4", + "2.9.0" /*latestTag("fomantic-ui")*/, // pin at 2.9.0 until https://github.com/phax/ph-css/issues/90 is fixed "https://cdn.jsdelivr.net/npm/fomantic-ui@" + _ + "/dist/semantic.min.css" ) +val fontawesomeUiConfig = + CssDslConfig( + "Font Awesome", + Set(None, Some("fa")), + latestTag("@fortawesome/fontawesome-free"), + "https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@" + _ + "/css/all.min.css" + ) + lazy val bootstrap3_scalajsreact = project.enablePlugins(ScalaJSPlugin, GeneratorPlugin).settings(scalaJsReactSettings(bootstrap3Config)) lazy val bootstrap3_scalatags = @@ -70,6 +114,11 @@ lazy val bootstrap4_scalajsreact = lazy val bootstrap4_scalatags = project.enablePlugins(GeneratorPlugin).settings(scalatagsSettings(bootstrap4Config)) +lazy val bootstrap5_scalajsreact = + project.enablePlugins(ScalaJSPlugin, GeneratorPlugin).settings(scalaJsReactSettings(bootstrap5Config)) +lazy val bootstrap5_scalatags = + project.enablePlugins(GeneratorPlugin).settings(scalatagsSettings(bootstrap5Config)) + lazy val bulma_scalajsreact = project.enablePlugins(ScalaJSPlugin, GeneratorPlugin).settings(scalaJsReactSettings(bulmaConfig)) lazy val bulma_scalatags = @@ -84,3 +133,8 @@ lazy val fomanticui_scalajsreact = project.enablePlugins(ScalaJSPlugin, GeneratorPlugin).settings(scalaJsReactSettings(fomanticUiConfig)) lazy val fomanticui_scalatags = project.enablePlugins(GeneratorPlugin).settings(scalatagsSettings(fomanticUiConfig)) + +lazy val fontawesome_scalajsreact = + project.enablePlugins(ScalaJSPlugin, GeneratorPlugin).settings(scalaJsReactSettings(fontawesomeUiConfig)) +lazy val fontawesome_scalatags = + project.enablePlugins(GeneratorPlugin).settings(scalatagsSettings(fontawesomeUiConfig)) diff --git a/ci.sbt b/ci.sbt new file mode 100644 index 0000000..5be6e71 --- /dev/null +++ b/ci.sbt @@ -0,0 +1,24 @@ +inThisBuild(List( + homepage := Some(url("https://github.com/nafg/css-dsl")), + licenses := List("Apache-2.0" -> url("https://www.apache.org/licenses/LICENSE-2.0")), + developers := List( + Developer("nafg", "Naftoli Gugenheim", "98384+nafg@users.noreply.github.com", url("https://github.com/nafg")) + ), + dynverGitDescribeOutput ~= (_.map(o => o.copy(dirtySuffix = sbtdynver.GitDirtySuffix("")))), + dynverSonatypeSnapshots := true, + githubWorkflowScalaVersions := githubWorkflowScalaVersions.value.map(_.replaceFirst("\\d+$", "x")), + githubWorkflowTargetTags ++= Seq("v*"), + githubWorkflowPublishTargetBranches := Seq(RefPredicate.StartsWith(Ref.Tag("v"))), + githubWorkflowJavaVersions := Seq(JavaSpec.temurin("11")), + githubWorkflowPublish := Seq( + WorkflowStep.Sbt( + List("ci-release"), + env = Map( + "PGP_PASSPHRASE" -> "${{ secrets.PGP_PASSPHRASE }}", + "PGP_SECRET" -> "${{ secrets.PGP_SECRET }}", + "SONATYPE_PASSWORD" -> "${{ secrets.SONATYPE_PASSWORD }}", + "SONATYPE_USERNAME" -> "${{ secrets.SONATYPE_USERNAME }}" + ) + ) + ) +)) diff --git a/generateInstructions.sbt b/generateInstructions.sbt new file mode 100644 index 0000000..2a380a0 --- /dev/null +++ b/generateInstructions.sbt @@ -0,0 +1,76 @@ +val generateInstallInstructions = taskKey[Unit]("Generate instructions in README.md") + +generateInstallInstructions := { + val info = + Def.task((projectID.value, cssDslConfig.value, (publish / skip).value)) + .all(ScopeFilter(inAnyProject -- inProjects(ThisProject))).value + .collect { case (id, cfg, false) => + (id, + id.name.split('_') match { + case Array(p, s) => (p, s) + }, + cfg) + } + .sortBy(_._2)(Ordering.Tuple2(Ordering.String, Ordering.String.reverse)) + + def mdTable(head: Seq[String], rows: Seq[Seq[String]]) = { + val widths = + (head.map(_.length) +: rows.map(_.map(_.length))).transpose.map(_.max) + .zipWithIndex.map(_.swap).toMap.withDefaultValue(0) + def row(xs: Seq[String]) = + xs.zipWithIndex.map { case (s, i) => s.padTo(widths(i), ' ') }.mkString("| ", " | ", " |\n") + row(head) + + widths.values.map("-" * _).mkString("|-", "-|-", "-|\n") + + rows.map(row).mkString + } + + def markerText(side: String) = s"" + + val artifactsTable = mdTable(List("CSS library", "Scala DOM library", "SBT Module ID"), info.map { + case (moduleId, (_, moduleNameSuffix), config) => + val (op, library) = moduleNameSuffix match { + case "scalatags" => "%%" -> "`scalatags.Text` (JVM)" + case "scalajsreact" => "%%%" -> "scalajs-react (scala.js)" + } + List( + config.name, + library, + s"""`"${moduleId.organization}" $op "${moduleId.name}" % "${moduleId.revision}"`""" + ) + }) + + val importsTable = + mdTable( + List("CSS library", "Prefix", "Import"), + info.flatMap { case (_, _, cfg) => + cfg.prefixes.map { p => + List( + cfg.name, + p.fold("None")("`" + _ + "`"), + "`import cssdsl." + cfg.scalaPackage + "." + p.fold("")(_.capitalize) + "Dsl._`" + ) + } + }.distinct + ) + + val block = + s""">${markerText("Begin")} + > + >#### Artifact + > + >$artifactsTable + > + >### Import + > + >$importsTable + > + >${markerText("End")} + >""".stripMargin('>') + + val readmeFile = baseDirectory.value / "README.md" + val currentReadme = IO.readLines(readmeFile) + val (before, rest) = currentReadme.span(_.trim != markerText("Begin")) + val after = rest.reverse.takeWhile(_.trim != markerText("End")).reverse + val newReadme = before.map(_ + "\n").mkString + block + after.map(_ + "\n").mkString + IO.write(readmeFile, newReadme) +} diff --git a/latest.sh b/latest.sh deleted file mode 100644 index 20227d7..0000000 --- a/latest.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -echo Latest bootstrap -npm view bootstrap dist-tags.latest - -echo Previous bootstrap -npm view bootstrap dist-tags.previous - -echo Latest Bulma -npm view bulma dist-tags.latest - -echo Latest Semantic UI -npm view semantic-ui dist-tags.latest - -echo Latest Fomantic UI -npm view fomantic-ui dist-tags.latest diff --git a/project/CssExtractor.scala b/project/CssExtractor.scala index 64b0f6b..4dfd548 100644 --- a/project/CssExtractor.scala +++ b/project/CssExtractor.scala @@ -1,29 +1,45 @@ import java.net.URL -import scala.collection.JavaConverters._ +import scala.collection.JavaConverters.* import scala.collection.immutable.SortedSet import com.helger.commons.collection.impl.ICommonsList import com.helger.commons.io.IHasReader -import com.helger.css.decl._ +import com.helger.css.decl.* import com.helger.css.reader.{CSSReader, CSSReaderSettings} object CssExtractor { private val hex = (('0' to '9') ++ ('a' to 'f') ++ ('A' to 'F')).toSet + private object Unicode { + def unapply(chars: List[Char]): Option[(Char, List[Char])] = { + val (first6, further) = chars.splitAt(6) + val (digits, beyond) = first6.span(hex) + if (digits.isEmpty) + None + else { + val c = Integer.parseInt(digits.mkString, 16).toChar + val rest = beyond ++ further + val more = + rest match { + case '\r' :: '\n' :: more => more + case w :: more if w.isWhitespace => more + case more => more + } + Some((c, more)) + } + } + } + //\\[^\r\n\f0-9a-f] + val unesapable = Set('\r', '\n', '\f') ++ ((0 to 9).map(_.toChar) ++ ('a' to 'f') ++ ('A' to 'F')) + def unescape(s: String) = { def loop(ch: List[Char]): List[Char] = ch match { - case Nil => Nil - case '\\' :: rest => - val (digits, beyond) = rest.span(hex) - val c = Integer.parseInt(digits.mkString, 16).toChar - beyond match { - case '\r' :: '\n' :: more => c :: loop(more) - case w :: more if w.isWhitespace => c :: loop(more) - case more => c :: loop(more) - } - case c :: rest => c :: loop(rest) + case Nil => Nil + case '\\' :: Unicode(c, beyond) => c :: loop(beyond) + case '\\' :: c :: rest if !unesapable(c) => c :: loop(rest) + case c :: rest => c :: loop(rest) } loop(s.toList).mkString @@ -59,6 +75,6 @@ object CssExtractor { def getClassesFromURL(url: URL): SortedSet[String] = { val reader: IHasReader = () => new java.io.InputStreamReader(url.openStream()) val sheet = CSSReader.readFromReader(reader, new CSSReaderSettings()) - SortedSet(getClassesFromSheet(sheet).toSeq: _*) + SortedSet(getClassesFromSheet(sheet).toSeq *) } } diff --git a/project/GeneratorPlugin.scala b/project/GeneratorPlugin.scala index e763836..5b46d08 100644 --- a/project/GeneratorPlugin.scala +++ b/project/GeneratorPlugin.scala @@ -4,10 +4,12 @@ import sbt._ object GeneratorPlugin extends AutoPlugin { object autoImport { - case class CssDslConfig(packageName: String, + case class CssDslConfig(name: String, prefixes: Set[Option[String]], version: String, - versionedUrl: String => String) + versionedUrl: String => String) { + def scalaPackage = name.toLowerCase.filter(Character.isJavaIdentifierPart) + } val cssDslConfig = settingKey[CssDslConfig]("The settings for generating the CSS DSL") val cssVariant = settingKey[TargetImpl]("The target") val cssGen = taskKey[Seq[File]]("Generate the DSL") @@ -21,11 +23,13 @@ object GeneratorPlugin extends AutoPlugin { val cfg = cssDslConfig.value val variant = cssVariant.value val outputDir = (Compile / sourceManaged).value - val classes = CssExtractor.getClassesFromURL(new URL(cfg.versionedUrl(cfg.version))) + val url = cfg.versionedUrl(cfg.version) + streams.value.log.info(s"Processing $url...") + val classes = CssExtractor.getClassesFromURL(new URL(url)) for (prefix <- cfg.prefixes.toSeq) yield { val name = prefix.getOrElse("").capitalize + "Dsl" - val generator = new Generator(cfg.packageName, name, prefix, classes, variant) - val file = outputDir / "cssdsl" / cfg.packageName.replace('.', '/') / s"$name.scala" + val generator = new Generator(cfg.scalaPackage, name, prefix, classes, variant) + val file = outputDir / "cssdsl" / cfg.scalaPackage.replace('.', '/') / s"$name.scala" IO.write(file, generator()) file } diff --git a/project/build.properties b/project/build.properties index b53dc26..1b248df 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version = 1.3.10 +sbt.version = 1.11.3 diff --git a/project/build.sbt b/project/build.sbt index 5d87b36..525aaf3 100644 --- a/project/build.sbt +++ b/project/build.sbt @@ -1,2 +1,2 @@ -libraryDependencies += "org.scalameta" %% "scalameta" % "4.3.10" -libraryDependencies += "com.helger" % "ph-css" % "6.2.2" +libraryDependencies += "org.scalameta" %% "scalameta" % "4.13.8" +libraryDependencies += "com.helger" % "ph-css" % "7.0.4" diff --git a/project/plugins.sbt b/project/plugins.sbt index 67d6db2..f3d910d 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,5 @@ -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.32") -addSbtPlugin("com.dwijnand" % "sbt-dynver" % "4.0.0") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.19.0") +addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % "0.21.1") +addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.11.1") +addSbtPlugin("io.github.nafg.mergify" % "sbt-mergify-github-actions" % "0.9.0") +libraryDependencies += "io.github.nafg.scalac-options" %% "scalac-options" % "0.3.0"