rouge-lexer-dotenv 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: cc8cf0f6c5267751bc39290acfc5892601a8013d3247b2b576c4791018e9a1c9
4
+ data.tar.gz: 20f371518f1fb9a6d43fe3a87a312c641baf5570851fe1d3eeaa5f11c5510ce3
5
+ SHA512:
6
+ metadata.gz: 91955e35d33d18058127f4ad14fce015993ca9e17c1d5cae17d75c496662b3e92c3eca1c99d80dd5248577dd131cb82b29ffd0840bc4ca9c1f5bace159fff4fc
7
+ data.tar.gz: 254dcad8a3ad1cba9069e13a35edfa60297bc8da6780c205153c8ac5d8d9ce39838cce243ae1795b0c4b68c52c70022005c5f6d4bc11a505cd9585cd8e77b17d
data/README.md ADDED
@@ -0,0 +1,129 @@
1
+ # rouge-lexer-dotenv
2
+
3
+ [![Test](https://github.com/seanthegeek/rouge-lexer-dotenv/actions/workflows/test.yml/badge.svg)](https://github.com/seanthegeek/rouge-lexer-dotenv/actions/workflows/test.yml)
4
+ [![Gem Version](https://badge.fury.io/rb/rouge-lexer-dotenv.svg)](https://rubygems.org/gems/rouge-lexer-dotenv)
5
+
6
+ A Rouge lexer plugin for dotenv (.env) environment variable files. Rouge is the
7
+ default syntax highlighter for Jekyll (and therefore GitHub Pages). This gem adds
8
+ .env support to Rouge.
9
+
10
+ ## Installation
11
+
12
+ The gem requires Ruby 3.0 or newer and Rouge 3.4 or newer.
13
+
14
+ Install the gem directly:
15
+
16
+ ```sh
17
+ gem install rouge-lexer-dotenv
18
+ ```
19
+
20
+ Or add it to your `Gemfile`:
21
+
22
+ ```ruby
23
+ gem 'rouge-lexer-dotenv'
24
+ ```
25
+
26
+ Then run:
27
+
28
+ ```sh
29
+ bundle install
30
+ ```
31
+
32
+ ## Usage
33
+
34
+ Once installed, Rouge will automatically discover the lexer. You can use
35
+ `dotenv` as the language tag in fenced code blocks (see the
36
+ lexer definition for additional aliases):
37
+
38
+ ````markdown
39
+ ```dotenv
40
+ # your code here
41
+ ```
42
+ ````
43
+
44
+ ### Jekyll / GitHub Pages
45
+
46
+ Add the gem to your site's `Gemfile` inside the `:jekyll_plugins` group:
47
+
48
+ ```ruby
49
+ group :jekyll_plugins do
50
+ gem "rouge-lexer-dotenv"
51
+ end
52
+ ```
53
+
54
+ Run `bundle install`, then use the language tag in fenced code blocks. Jekyll
55
+ will pick up the lexer automatically via Rouge's plugin discovery.
56
+
57
+ ````markdown
58
+ ```dotenv
59
+ # Database
60
+ DATABASE_URL="postgres://user:pass@localhost:5432/mydb"
61
+ DATABASE_POOL_SIZE=10
62
+
63
+ BASE_URL=https://api.example.com
64
+ FULL_URL=${BASE_URL}/v1/users
65
+ ```
66
+ ````
67
+
68
+ The aliases `env`, `.env` and `environment` work too:
69
+
70
+ ````markdown
71
+ ```env
72
+ ENABLE_CACHE=true
73
+ ```
74
+ ````
75
+
76
+ ### Colors
77
+
78
+ The lexer tells Rouge how to identify tokens. Rouge wraps each token in a `span` tag
79
+ with a `class` related to that token type. If you want to change how the tokens are
80
+ highlighted, change themes or add custom CSS.
81
+
82
+ ## Development
83
+
84
+ Install dependencies:
85
+
86
+ ```sh
87
+ bundle install
88
+ ```
89
+
90
+ Run the test suite:
91
+
92
+ ```sh
93
+ bundle exec rake
94
+ ```
95
+
96
+ Start the visual preview server (available at http://localhost:9292):
97
+
98
+ ```sh
99
+ bundle exec rake server
100
+ ```
101
+
102
+ Run the terminal preview script:
103
+
104
+ ```sh
105
+ ruby preview.rb
106
+ ```
107
+
108
+ Enable debug mode to print each token and its value:
109
+
110
+ ```sh
111
+ DEBUG=1 ruby preview.rb
112
+ ```
113
+
114
+ ### Iterative testing workflow
115
+
116
+ 1. Run `bundle exec rake` to check for test failures and error tokens.
117
+ 2. Start the server with `bundle exec rake server`.
118
+ 3. In another terminal, check for error tokens in the rendered output:
119
+
120
+ ```sh
121
+ curl -s http://localhost:9292 | grep 'class="err"'
122
+ ```
123
+
124
+ 4. Fix any error tokens in `lib/rouge/lexers/dotenv.rb`.
125
+ 5. Repeat until no error tokens remain.
126
+
127
+ ## License
128
+
129
+ MIT
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rouge'
4
+ require File.expand_path('../lexers/dotenv', __dir__)
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Rouge lexer for dotenv (.env) environment variable files.
4
+ #
5
+ # Every construct recognised here is taken from the env.dev dotenv guide
6
+ # (https://env.dev/guides/dotenv): KEY=value assignments, `#` comments,
7
+ # double-quoted values (spaces, special characters, multiline and `${VAR}`
8
+ # expansion), single-quoted literal values, empty values and `${VAR}`
9
+ # expansion in unquoted values. See AGENTS.md for the design notes.
10
+ module Rouge
11
+ module Lexers
12
+ class Dotenv < RegexLexer
13
+ title 'dotenv'
14
+ desc 'dotenv (.env) environment variable files (env.dev/guides/dotenv)'
15
+ tag 'dotenv'
16
+ aliases 'env', '.env', 'environment'
17
+ # `.env.test` also matches the PHP lexer's `*.test` (Drupal) pattern and
18
+ # cannot be disambiguated by filename alone; see the project notes in
19
+ # AGENTS.md. Passing the source to Rouge::Lexer.guess resolves it.
20
+ filenames '.env', '.env.*', '*.env'
21
+ mimetypes 'text/x-dotenv'
22
+
23
+ # Boolean values, as used by feature flags in the documentation
24
+ # (ENABLE_CACHE=true). Only recognised when they are the whole value.
25
+ def self.constants
26
+ @constants ||= Set.new %w(true false)
27
+ end
28
+
29
+ # A .env file is a sequence of KEY=value lines (uppercase keys by
30
+ # convention, no whitespace around the `=`), blank lines and `#`
31
+ # comments. Require the first few meaningful lines to look like that.
32
+ def self.detect?(text)
33
+ lines = text.lines.map(&:strip).reject { |l| l.empty? || l.start_with?('#') }.first(5)
34
+ return false if lines.empty?
35
+
36
+ lines.all? { |l| l.match?(/\A[A-Z][A-Z0-9_]*=/) }
37
+ end
38
+
39
+ # Lookahead: nothing but optional blanks and an optional inline comment
40
+ # remain on the line, so the token just matched is the whole value.
41
+ END_OF_VALUE = /(?=[ \t]*(?:#.*)?$)/.freeze
42
+
43
+ state :whitespace do
44
+ rule %r/\s+/, Text::Whitespace
45
+ end
46
+
47
+ state :comment do
48
+ rule %r/#.*/, Comment::Single
49
+ end
50
+
51
+ state :root do
52
+ mixin :whitespace
53
+ mixin :comment
54
+
55
+ # KEY=value. Whitespace around `=` is a documented pitfall that breaks
56
+ # most parsers; it is still tokenised so a stray space does not turn
57
+ # the whole line into an error.
58
+ rule %r/([A-Za-z_][A-Za-z0-9_]*)([ \t]*)(=)/ do
59
+ groups Name::Variable, Text::Whitespace, Operator
60
+ push :value
61
+ end
62
+ end
63
+
64
+ # Everything after `=` up to the end of the line.
65
+ state :value do
66
+ rule %r/\n/, Text::Whitespace, :pop!
67
+ rule %r/[ \t]+/, Text::Whitespace
68
+
69
+ # Inline comment. Only reachable at a token boundary, i.e. right after
70
+ # `=` or after whitespace; a `#` glued to an unquoted value (the
71
+ # documented URL=https://x.com#anchor pitfall) stays part of the value.
72
+ mixin :comment
73
+
74
+ rule %r/"/, Str::Double, :double_string
75
+ rule %r/'/, Str::Single, :single_string
76
+
77
+ mixin :interpolation
78
+
79
+ rule %r/\d+#{END_OF_VALUE}/, Num::Integer
80
+
81
+ rule %r/[A-Za-z]+#{END_OF_VALUE}/ do |m|
82
+ if self.class.constants.include?(m[0])
83
+ token Keyword::Constant
84
+ else
85
+ token Str
86
+ end
87
+ end
88
+
89
+ # Unquoted value text, stopping at whitespace and at `${` expansions
90
+ rule %r/[^\s$]+/, Str
91
+ rule %r/\$/, Str
92
+ end
93
+
94
+ # Double quotes allow spaces, special characters, multiline values and
95
+ # `${VAR}` expansion.
96
+ state :double_string do
97
+ rule %r/"/, Str::Double, :pop!
98
+ rule %r/\\./m, Str::Escape
99
+ mixin :interpolation
100
+ rule %r/[^"\\$]+/m, Str::Double
101
+ rule %r/\$/, Str::Double
102
+ end
103
+
104
+ # Single quotes are literal: no escapes, no interpolation.
105
+ state :single_string do
106
+ rule %r/'/, Str::Single, :pop!
107
+ rule %r/[^']+/m, Str::Single
108
+ end
109
+
110
+ state :interpolation do
111
+ rule %r/\$\{/, Str::Interpol, :curly
112
+ end
113
+
114
+ state :curly do
115
+ rule %r/\}/, Str::Interpol, :pop!
116
+ rule %r/[A-Za-z_][A-Za-z0-9_]*/, Name::Variable
117
+ # An unterminated `${` ends at the line break; leave the newline to the
118
+ # enclosing state so it can close the value as well.
119
+ rule(/(?=\n)/) { pop! }
120
+ rule %r/[ \t]+/, Text::Whitespace
121
+ end
122
+ end
123
+ end
124
+ end
data/spec/demos/dotenv ADDED
@@ -0,0 +1,15 @@
1
+ # App
2
+ NODE_ENV=development
3
+ PORT=3000
4
+
5
+ # Database
6
+ DATABASE_URL="postgres://user:pass@localhost:5432/mydb"
7
+ DATABASE_POOL_SIZE=10
8
+
9
+ # External APIs
10
+ STRIPE_SECRET_KEY=sk_test_abc123
11
+ SENDGRID_API_KEY="SG.xxxx"
12
+ # Feature flags
13
+ ENABLE_CACHE=true
14
+ BASE_URL=https://api.example.com
15
+ FULL_URL=${BASE_URL}/v1/users
@@ -0,0 +1,111 @@
1
+ # Visual sample for the dotenv lexer. Every construct below comes from the
2
+ # env.dev dotenv guide: https://env.dev/guides/dotenv
3
+
4
+ # ---------------------------------------------------------------------------
5
+ # Basic assignment: KEY=value, uppercase keys, no spaces around the `=`
6
+ # ---------------------------------------------------------------------------
7
+ DATABASE_HOST=localhost
8
+ PORT=3000
9
+ NODE_ENV=development
10
+ LOG_LEVEL=debug
11
+ STRIPE_SECRET_KEY=sk_test_abc123
12
+ BASE_URL=https://api.example.com
13
+
14
+ # Integer values
15
+ DATABASE_POOL_SIZE=10
16
+ MAX_RETRIES=5
17
+ TIMEOUT_MS=30000
18
+
19
+ # Feature flags (boolean values)
20
+ ENABLE_CACHE=true
21
+ ENABLE_TRACING=false
22
+
23
+ # Lowercase and mixed-case keys are accepted even though uppercase is the
24
+ # convention
25
+ db_host=localhost
26
+ apiKey=abc123
27
+ _PRIVATE=1
28
+
29
+ # ---------------------------------------------------------------------------
30
+ # Quoted values
31
+ # ---------------------------------------------------------------------------
32
+
33
+ # Double quotes allow spaces and special characters
34
+ GREETING="Hello, World!"
35
+ DATABASE_URL="postgres://user:pass@localhost:5432/mydb"
36
+ SENDGRID_API_KEY="SG.xxxx"
37
+ MESSAGE="It's a \"quoted\" word with a tab\tand a backslash \\"
38
+ DOUBLE_TRUE="true"
39
+ DOUBLE_NUMBER="3000"
40
+
41
+ # Single quotes treat the value as a literal string with no interpolation
42
+ REGEX='\d+\.\d+'
43
+ LITERAL='${NOT_EXPANDED} stays as written'
44
+ QUOTE='He said "hi"'
45
+ SINGLE_HASH='# not a comment'
46
+
47
+ # ---------------------------------------------------------------------------
48
+ # Comments and empty values
49
+ # ---------------------------------------------------------------------------
50
+
51
+ # Database config
52
+ DB_PASSWORD=
53
+ DB_NAME=""
54
+ DB_USER=''
55
+
56
+ # Inline comments are supported by some parsers but not all
57
+ REDIS_HOST=localhost # local development only
58
+ REDIS_PORT=6379 # default port
59
+ DEBUG=true # feature flag
60
+ CACHE_TTL="60" # quoted number
61
+
62
+ # An unquoted `#` with no space in front of it is part of the value; many
63
+ # parsers truncate here, so the guide recommends quoting the value instead
64
+ DOCS_URL=https://x.com#anchor
65
+ DOCS_URL_QUOTED="https://x.com#anchor"
66
+
67
+ # ---------------------------------------------------------------------------
68
+ # Multiline and variable expansion
69
+ # ---------------------------------------------------------------------------
70
+
71
+ # Some parsers support multiline values in double quotes
72
+ PRIVATE_KEY="-----BEGIN RSA KEY-----
73
+ MIIBogIBAAJBALRi...
74
+ -----END RSA KEY-----"
75
+
76
+ CERTIFICATE="-----BEGIN CERTIFICATE-----
77
+ MIIC+zCCAeOgAwIBAgIJAL
78
+ -----END CERTIFICATE-----"
79
+
80
+ # Variable expansion with the ${} syntax
81
+ BASE_URL=https://api.example.com
82
+ FULL_URL=${BASE_URL}/v1/users
83
+ API_ROOT=${BASE_URL}
84
+ HEALTH_URL=${BASE_URL}/health # inline comment after expansion
85
+ PREFIX_AND_SUFFIX=prefix-${NODE_ENV}-suffix
86
+ TWO_VARIABLES=${DATABASE_HOST}:${PORT}
87
+
88
+ # Expansion inside double quotes
89
+ DATABASE_URL="postgres://user:${DB_PASSWORD}@${DATABASE_HOST}:5432/${DB_NAME}"
90
+ GREETING_FULL="${GREETING} from ${NODE_ENV}"
91
+
92
+ # A lone dollar sign that is not an expansion is plain text
93
+ PRICE=$5
94
+ PRICE_QUOTED="costs $5"
95
+
96
+ # ---------------------------------------------------------------------------
97
+ # Whitespace and per-environment values
98
+ # ---------------------------------------------------------------------------
99
+
100
+ # Whitespace around `=` breaks most parsers; it is tokenised but not endorsed
101
+ SPACED_KEY = value
102
+ TAB_INDENTED=value
103
+ SPACE_INDENTED=value
104
+
105
+ # Values typical of a committed .env.example template
106
+ SECRET_KEY=replace-me
107
+ SENTRY_DSN=
108
+ AWS_REGION=us-east-1
109
+ S3_BUCKET=my-bucket.example.com
110
+ ALLOWED_HOSTS=localhost,127.0.0.1,example.com
111
+ CORS_ORIGINS="http://localhost:3000, https://app.example.com"
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rouge-lexer-dotenv
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sean Whalen
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rouge
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '3.4'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '3.4'
26
+ description: A Rouge plugin providing syntax highlighting for dotenv (.env) environment
27
+ variable files
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - README.md
33
+ - lib/rouge/lexer/dotenv.rb
34
+ - lib/rouge/lexers/dotenv.rb
35
+ - spec/demos/dotenv
36
+ - spec/visual/samples/dotenv
37
+ homepage: https://github.com/seanthegeek/rouge-lexer-dotenv
38
+ licenses:
39
+ - MIT
40
+ metadata:
41
+ source_code_uri: https://github.com/seanthegeek/rouge-lexer-dotenv
42
+ bug_tracker_uri: https://github.com/seanthegeek/rouge-lexer-dotenv/issues
43
+ changelog_uri: https://github.com/seanthegeek/rouge-lexer-dotenv/blob/main/CHANGELOG.md
44
+ documentation_uri: https://github.com/seanthegeek/rouge-lexer-dotenv/blob/main/README.md
45
+ rdoc_options: []
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '3.0'
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubygems_version: 3.6.9
60
+ specification_version: 4
61
+ summary: Rouge lexer for .env
62
+ test_files: []